Call Now Button - Version 1.1.9

Version Description

  • Introduced exit surveys
  • Error & usage reporting setting (off by default)
  • Email subscription setting (Premium)
  • Small fixes/improvements
Download this release

Release Info

Developer jasperroel
Plugin Icon 128x128 Call Now Button
Version 1.1.9
Comparing to
See all releases

Code changes from version 1.1.8 to 1.1.9

Files changed (273) hide show
  1. call-now-button.php +4 -2
  2. readme.txt +7 -1
  3. resources/js/call-now-button.js +8 -1
  4. resources/js/condition-edit.js +75 -26
  5. resources/js/deactivation.js +39 -0
  6. resources/js/error-reporting.js +62 -0
  7. resources/js/preview.js +41 -40
  8. resources/js/settings.js +32 -1
  9. src/CallNowButton.php +145 -138
  10. src/admin/CnbAdminAjax.php +31 -5
  11. src/admin/action/CnbActionController.php +19 -21
  12. src/admin/action/CnbActionRouter.php +3 -1
  13. src/admin/api/CnbAppRemote.php +48 -19
  14. src/admin/api/RemoteTrace.php +18 -1
  15. src/admin/apikey/CnbApiKeyController.php +34 -26
  16. src/admin/apikey/CnbApiKeyRouter.php +3 -1
  17. src/admin/button/CnbButton.php +55 -2
  18. src/admin/button/CnbButtonController.php +118 -108
  19. src/admin/button/CnbButtonRouter.php +3 -1
  20. src/admin/button/CnbButtonView.php +2 -2
  21. src/admin/button/CnbButtonViewEdit.php +34 -3
  22. src/admin/condition/CnbCondition.php +7 -0
  23. src/admin/condition/CnbConditionController.php +25 -13
  24. src/admin/condition/CnbConditionRouter.php +3 -1
  25. src/admin/condition/CnbConditionViewEdit.php +25 -4
  26. src/admin/deactivation/Deactivation.php +28 -0
  27. src/admin/domain/CnbDomainController.php +105 -83
  28. src/admin/domain/CnbDomainRouter.php +3 -1
  29. src/admin/domain/partials/CnbDomainViewUpgradeFinished.php +8 -7
  30. src/admin/domain/partials/CnbDomainViewUpgradeOverview.php +7 -6
  31. src/admin/legacy/CnbLegacyEdit.php +216 -212
  32. src/admin/legacy/CnbLegacyUpgrade.php +7 -6
  33. src/admin/models/CnbUser.php +55 -0
  34. src/admin/partials/CnbFooter.php +92 -29
  35. src/admin/partials/CnbHeader.php +26 -13
  36. src/admin/partials/CnbHeaderNotices.php +0 -1
  37. src/admin/profile/CnbProfileController.php +6 -4
  38. src/admin/profile/CnbProfileEdit.php +3 -5
  39. src/admin/profile/CnbProfileRouter.php +14 -0
  40. src/admin/settings/CnbSettingsController.php +48 -7
  41. src/admin/settings/CnbSettingsRouter.php +3 -1
  42. src/admin/settings/CnbSettingsViewEdit.php +89 -35
  43. src/admin/settings/StripeBillingPortal.php +26 -0
  44. src/autoload.php +6 -0
  45. src/call-now-button.php +14 -8
  46. src/composer.json +6 -0
  47. src/composer.lock +1884 -0
  48. src/utils/CnbAdminFunctions.php +25 -2
  49. src/utils/CnbUtils.php +5 -23
  50. src/utils/class-cachehandler.php +1 -1
  51. src/utils/class-cnb-sentry.php +204 -0
  52. src/vendor/autoload.php +12 -0
  53. src/vendor/clue/stream-filter/.github/FUNDING.yml +2 -0
  54. src/vendor/clue/stream-filter/CHANGELOG.md +86 -0
  55. src/vendor/clue/stream-filter/LICENSE +21 -0
  56. src/vendor/clue/stream-filter/README.md +326 -0
  57. src/vendor/clue/stream-filter/composer.json +26 -0
  58. src/vendor/clue/stream-filter/src/CallbackFilter.php +120 -0
  59. src/vendor/clue/stream-filter/src/functions.php +327 -0
  60. src/vendor/clue/stream-filter/src/functions_include.php +6 -0
  61. src/vendor/composer/ClassLoader.php +572 -0
  62. src/vendor/composer/InstalledVersions.php +352 -0
  63. src/vendor/composer/LICENSE +19 -0
  64. src/vendor/composer/autoload_classmap.php +16 -0
  65. src/vendor/composer/autoload_files.php +18 -0
  66. src/vendor/composer/autoload_namespaces.php +9 -0
  67. src/vendor/composer/autoload_psr4.php +31 -0
  68. src/vendor/composer/autoload_real.php +57 -0
  69. src/vendor/composer/autoload_static.php +176 -0
  70. src/vendor/composer/installed.json +1949 -0
  71. src/vendor/composer/installed.php +309 -0
  72. src/vendor/composer/platform_check.php +26 -0
  73. src/vendor/guzzlehttp/promises/CHANGELOG.md +103 -0
  74. src/vendor/guzzlehttp/promises/LICENSE +24 -0
  75. src/vendor/guzzlehttp/promises/Makefile +13 -0
  76. src/vendor/guzzlehttp/promises/README.md +547 -0
  77. src/vendor/guzzlehttp/promises/composer.json +58 -0
  78. src/vendor/guzzlehttp/promises/src/AggregateException.php +17 -0
  79. src/vendor/guzzlehttp/promises/src/CancellationException.php +10 -0
  80. src/vendor/guzzlehttp/promises/src/Coroutine.php +169 -0
  81. src/vendor/guzzlehttp/promises/src/Create.php +84 -0
  82. src/vendor/guzzlehttp/promises/src/Each.php +90 -0
  83. src/vendor/guzzlehttp/promises/src/EachPromise.php +255 -0
  84. src/vendor/guzzlehttp/promises/src/FulfilledPromise.php +84 -0
  85. src/vendor/guzzlehttp/promises/src/Is.php +46 -0
  86. src/vendor/guzzlehttp/promises/src/Promise.php +278 -0
  87. src/vendor/guzzlehttp/promises/src/PromiseInterface.php +97 -0
  88. src/vendor/guzzlehttp/promises/src/PromisorInterface.php +16 -0
  89. src/vendor/guzzlehttp/promises/src/RejectedPromise.php +91 -0
  90. src/vendor/guzzlehttp/promises/src/RejectionException.php +48 -0
  91. src/vendor/guzzlehttp/promises/src/TaskQueue.php +67 -0
  92. src/vendor/guzzlehttp/promises/src/TaskQueueInterface.php +24 -0
  93. src/vendor/guzzlehttp/promises/src/Utils.php +276 -0
  94. src/vendor/guzzlehttp/promises/src/functions.php +363 -0
  95. src/vendor/guzzlehttp/promises/src/functions_include.php +6 -0
  96. src/vendor/guzzlehttp/psr7/CHANGELOG.md +371 -0
  97. src/vendor/guzzlehttp/psr7/LICENSE +26 -0
  98. src/vendor/guzzlehttp/psr7/README.md +840 -0
  99. src/vendor/guzzlehttp/psr7/composer.json +92 -0
  100. src/vendor/guzzlehttp/psr7/src/AppendStream.php +249 -0
  101. src/vendor/guzzlehttp/psr7/src/BufferStream.php +149 -0
  102. src/vendor/guzzlehttp/psr7/src/CachingStream.php +148 -0
  103. src/vendor/guzzlehttp/psr7/src/DroppingStream.php +46 -0
  104. src/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php +14 -0
  105. src/vendor/guzzlehttp/psr7/src/FnStream.php +179 -0
  106. src/vendor/guzzlehttp/psr7/src/Header.php +134 -0
  107. src/vendor/guzzlehttp/psr7/src/HttpFactory.php +100 -0
  108. src/vendor/guzzlehttp/psr7/src/InflateStream.php +34 -0
  109. src/vendor/guzzlehttp/psr7/src/LazyOpenStream.php +40 -0
  110. src/vendor/guzzlehttp/psr7/src/LimitStream.php +154 -0
  111. src/vendor/guzzlehttp/psr7/src/Message.php +245 -0
  112. src/vendor/guzzlehttp/psr7/src/MessageTrait.php +264 -0
  113. src/vendor/guzzlehttp/psr7/src/MimeType.php +1237 -0
  114. src/vendor/guzzlehttp/psr7/src/MultipartStream.php +156 -0
  115. src/vendor/guzzlehttp/psr7/src/NoSeekStream.php +25 -0
  116. src/vendor/guzzlehttp/psr7/src/PumpStream.php +179 -0
  117. src/vendor/guzzlehttp/psr7/src/Query.php +113 -0
  118. src/vendor/guzzlehttp/psr7/src/Request.php +157 -0
  119. src/vendor/guzzlehttp/psr7/src/Response.php +160 -0
  120. src/vendor/guzzlehttp/psr7/src/Rfc7230.php +23 -0
  121. src/vendor/guzzlehttp/psr7/src/ServerRequest.php +344 -0
  122. src/vendor/guzzlehttp/psr7/src/Stream.php +282 -0
  123. src/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php +155 -0
  124. src/vendor/guzzlehttp/psr7/src/StreamWrapper.php +175 -0
  125. src/vendor/guzzlehttp/psr7/src/UploadedFile.php +211 -0
  126. src/vendor/guzzlehttp/psr7/src/Uri.php +738 -0
  127. src/vendor/guzzlehttp/psr7/src/UriNormalizer.php +220 -0
  128. src/vendor/guzzlehttp/psr7/src/UriResolver.php +211 -0
  129. src/vendor/guzzlehttp/psr7/src/Utils.php +459 -0
  130. src/vendor/http-interop/http-factory-guzzle/.github/workflows/ci.yaml +60 -0
  131. src/vendor/http-interop/http-factory-guzzle/LICENSE +21 -0
  132. src/vendor/http-interop/http-factory-guzzle/README.md +7 -0
  133. src/vendor/http-interop/http-factory-guzzle/composer.json +37 -0
  134. src/vendor/http-interop/http-factory-guzzle/src/RequestFactory.php +15 -0
  135. src/vendor/http-interop/http-factory-guzzle/src/ResponseFactory.php +15 -0
  136. src/vendor/http-interop/http-factory-guzzle/src/ServerRequestFactory.php +24 -0
  137. src/vendor/http-interop/http-factory-guzzle/src/StreamFactory.php +26 -0
  138. src/vendor/http-interop/http-factory-guzzle/src/UploadedFileFactory.php +25 -0
  139. src/vendor/http-interop/http-factory-guzzle/src/UriFactory.php +15 -0
  140. src/vendor/jean85/pretty-package-versions/.github/workflows/tests.yaml +76 -0
  141. src/vendor/jean85/pretty-package-versions/.php_cs.dist +118 -0
  142. src/vendor/jean85/pretty-package-versions/LICENSE +21 -0
  143. src/vendor/jean85/pretty-package-versions/Makefile +19 -0
  144. src/vendor/jean85/pretty-package-versions/codecov.yml +1 -0
  145. src/vendor/jean85/pretty-package-versions/composer.json +50 -0
  146. src/vendor/jean85/pretty-package-versions/phpstan.neon +5 -0
  147. src/vendor/jean85/pretty-package-versions/psalm.xml +16 -0
  148. src/vendor/jean85/pretty-package-versions/src/Exception/ProvidedPackageException.php +13 -0
  149. src/vendor/jean85/pretty-package-versions/src/Exception/ReplacedPackageException.php +13 -0
  150. src/vendor/jean85/pretty-package-versions/src/Exception/VersionMissingExceptionInterface.php +10 -0
  151. src/vendor/jean85/pretty-package-versions/src/PrettyVersions.php +77 -0
  152. src/vendor/jean85/pretty-package-versions/src/Version.php +101 -0
  153. src/vendor/php-http/client-common/.php_cs.dist +24 -0
  154. src/vendor/php-http/client-common/CHANGELOG.md +276 -0
  155. src/vendor/php-http/client-common/LICENSE +19 -0
  156. src/vendor/php-http/client-common/README.md +55 -0
  157. src/vendor/php-http/client-common/composer.json +67 -0
  158. src/vendor/php-http/client-common/src/BatchClient.php +42 -0
  159. src/vendor/php-http/client-common/src/BatchClientInterface.php +34 -0
  160. src/vendor/php-http/client-common/src/BatchResult.php +157 -0
  161. src/vendor/php-http/client-common/src/Deferred.php +152 -0
  162. src/vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php +25 -0
  163. src/vendor/php-http/client-common/src/EmulatedHttpClient.php +24 -0
  164. src/vendor/php-http/client-common/src/Exception/BatchException.php +37 -0
  165. src/vendor/php-http/client-common/src/Exception/CircularRedirectionException.php +16 -0
  166. src/vendor/php-http/client-common/src/Exception/ClientErrorException.php +16 -0
  167. src/vendor/php-http/client-common/src/Exception/HttpClientNoMatchException.php +33 -0
  168. src/vendor/php-http/client-common/src/Exception/HttpClientNotFoundException.php +16 -0
  169. src/vendor/php-http/client-common/src/Exception/LoopException.php +16 -0
  170. src/vendor/php-http/client-common/src/Exception/MultipleRedirectionException.php +16 -0
  171. src/vendor/php-http/client-common/src/Exception/ServerErrorException.php +16 -0
  172. src/vendor/php-http/client-common/src/FlexibleHttpClient.php +36 -0
  173. src/vendor/php-http/client-common/src/HttpAsyncClientDecorator.php +31 -0
  174. src/vendor/php-http/client-common/src/HttpAsyncClientEmulator.php +39 -0
  175. src/vendor/php-http/client-common/src/HttpClientDecorator.php +32 -0
  176. src/vendor/php-http/client-common/src/HttpClientEmulator.php +35 -0
  177. src/vendor/php-http/client-common/src/HttpClientPool.php +24 -0
  178. src/vendor/php-http/client-common/src/HttpClientPool/HttpClientPool.php +70 -0
  179. src/vendor/php-http/client-common/src/HttpClientPool/HttpClientPoolItem.php +181 -0
  180. src/vendor/php-http/client-common/src/HttpClientPool/LeastUsedClientPool.php +45 -0
  181. src/vendor/php-http/client-common/src/HttpClientPool/RandomClientPool.php +31 -0
  182. src/vendor/php-http/client-common/src/HttpClientPool/RoundRobinClientPool.php +42 -0
  183. src/vendor/php-http/client-common/src/HttpClientRouter.php +74 -0
  184. src/vendor/php-http/client-common/src/HttpClientRouterInterface.php +27 -0
  185. src/vendor/php-http/client-common/src/HttpMethodsClient.php +150 -0
  186. src/vendor/php-http/client-common/src/HttpMethodsClientInterface.php +116 -0
  187. src/vendor/php-http/client-common/src/Plugin.php +33 -0
  188. src/vendor/php-http/client-common/src/Plugin/AddHostPlugin.php +76 -0
  189. src/vendor/php-http/client-common/src/Plugin/AddPathPlugin.php +78 -0
  190. src/vendor/php-http/client-common/src/Plugin/AuthenticationPlugin.php +38 -0
  191. src/vendor/php-http/client-common/src/Plugin/BaseUriPlugin.php +57 -0
  192. src/vendor/php-http/client-common/src/Plugin/ContentLengthPlugin.php +39 -0
  193. src/vendor/php-http/client-common/src/Plugin/ContentTypePlugin.php +122 -0
  194. src/vendor/php-http/client-common/src/Plugin/CookiePlugin.php +180 -0
  195. src/vendor/php-http/client-common/src/Plugin/DecoderPlugin.php +135 -0
  196. src/vendor/php-http/client-common/src/Plugin/ErrorPlugin.php +92 -0
  197. src/vendor/php-http/client-common/src/Plugin/HeaderAppendPlugin.php +48 -0
  198. src/vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php +46 -0
  199. src/vendor/php-http/client-common/src/Plugin/HeaderRemovePlugin.php +44 -0
  200. src/vendor/php-http/client-common/src/Plugin/HeaderSetPlugin.php +44 -0
  201. src/vendor/php-http/client-common/src/Plugin/HistoryPlugin.php +49 -0
  202. src/vendor/php-http/client-common/src/Plugin/Journal.php +33 -0
  203. src/vendor/php-http/client-common/src/Plugin/QueryDefaultsPlugin.php +50 -0
  204. src/vendor/php-http/client-common/src/Plugin/RedirectPlugin.php +265 -0
  205. src/vendor/php-http/client-common/src/Plugin/RequestMatcherPlugin.php +56 -0
  206. src/vendor/php-http/client-common/src/Plugin/RequestSeekableBodyPlugin.php +29 -0
  207. src/vendor/php-http/client-common/src/Plugin/ResponseSeekableBodyPlugin.php +32 -0
  208. src/vendor/php-http/client-common/src/Plugin/RetryPlugin.php +179 -0
  209. src/vendor/php-http/client-common/src/Plugin/SeekableBodyPlugin.php +47 -0
  210. src/vendor/php-http/client-common/src/Plugin/VersionBridgePlugin.php +24 -0
  211. src/vendor/php-http/client-common/src/PluginChain.php +62 -0
  212. src/vendor/php-http/client-common/src/PluginClient.php +130 -0
  213. src/vendor/php-http/client-common/src/PluginClientBuilder.php +76 -0
  214. src/vendor/php-http/client-common/src/PluginClientFactory.php +68 -0
  215. src/vendor/php-http/client-common/src/VersionBridgeClient.php +24 -0
  216. src/vendor/php-http/discovery/.php-cs-fixer.php +16 -0
  217. src/vendor/php-http/discovery/CHANGELOG.md +319 -0
  218. src/vendor/php-http/discovery/LICENSE +19 -0
  219. src/vendor/php-http/discovery/README.md +46 -0
  220. src/vendor/php-http/discovery/composer.json +49 -0
  221. src/vendor/php-http/discovery/src/ClassDiscovery.php +252 -0
  222. src/vendor/php-http/discovery/src/Exception.php +12 -0
  223. src/vendor/php-http/discovery/src/Exception/ClassInstantiationFailedException.php +14 -0
  224. src/vendor/php-http/discovery/src/Exception/DiscoveryFailedException.php +51 -0
  225. src/vendor/php-http/discovery/src/Exception/NoCandidateFoundException.php +47 -0
  226. src/vendor/php-http/discovery/src/Exception/NotFoundException.php +16 -0
  227. src/vendor/php-http/discovery/src/Exception/PuliUnavailableException.php +12 -0
  228. src/vendor/php-http/discovery/src/Exception/StrategyUnavailableException.php +15 -0
  229. src/vendor/php-http/discovery/src/HttpAsyncClientDiscovery.php +32 -0
  230. src/vendor/php-http/discovery/src/HttpClientDiscovery.php +32 -0
  231. src/vendor/php-http/discovery/src/MessageFactoryDiscovery.php +34 -0
  232. src/vendor/php-http/discovery/src/NotFoundException.php +14 -0
  233. src/vendor/php-http/discovery/src/Psr17FactoryDiscovery.php +136 -0
  234. src/vendor/php-http/discovery/src/Psr18ClientDiscovery.php +32 -0
  235. src/vendor/php-http/discovery/src/Strategy/CommonClassesStrategy.php +189 -0
  236. src/vendor/php-http/discovery/src/Strategy/CommonPsr17ClassesStrategy.php +105 -0
  237. src/vendor/php-http/discovery/src/Strategy/DiscoveryStrategy.php +23 -0
  238. src/vendor/php-http/discovery/src/Strategy/MockClientStrategy.php +27 -0
  239. src/vendor/php-http/discovery/src/Strategy/PuliBetaStrategy.php +92 -0
  240. src/vendor/php-http/discovery/src/StreamFactoryDiscovery.php +34 -0
  241. src/vendor/php-http/discovery/src/UriFactoryDiscovery.php +34 -0
  242. src/vendor/php-http/httplug/.php-cs-fixer.dist.php +16 -0
  243. src/vendor/php-http/httplug/CHANGELOG.md +136 -0
  244. src/vendor/php-http/httplug/LICENSE +20 -0
  245. src/vendor/php-http/httplug/README.md +62 -0
  246. src/vendor/php-http/httplug/composer.json +45 -0
  247. src/vendor/php-http/httplug/puli.json +12 -0
  248. src/vendor/php-http/httplug/src/Exception.php +14 -0
  249. src/vendor/php-http/httplug/src/Exception/HttpException.php +65 -0
  250. src/vendor/php-http/httplug/src/Exception/NetworkException.php +28 -0
  251. src/vendor/php-http/httplug/src/Exception/RequestAwareTrait.php +26 -0
  252. src/vendor/php-http/httplug/src/Exception/RequestException.php +29 -0
  253. src/vendor/php-http/httplug/src/Exception/TransferException.php +14 -0
  254. src/vendor/php-http/httplug/src/HttpAsyncClient.php +25 -0
  255. src/vendor/php-http/httplug/src/HttpClient.php +15 -0
  256. src/vendor/php-http/httplug/src/Promise/HttpFulfilledPromise.php +54 -0
  257. src/vendor/php-http/httplug/src/Promise/HttpRejectedPromise.php +58 -0
  258. src/vendor/php-http/message-factory/CHANGELOG.md +65 -0
  259. src/vendor/php-http/message-factory/LICENSE +19 -0
  260. src/vendor/php-http/message-factory/README.md +36 -0
  261. src/vendor/php-http/message-factory/composer.json +27 -0
  262. src/vendor/php-http/message-factory/puli.json +43 -0
  263. src/vendor/php-http/message-factory/src/MessageFactory.php +12 -0
  264. src/vendor/php-http/message-factory/src/RequestFactory.php +34 -0
  265. src/vendor/php-http/message-factory/src/ResponseFactory.php +35 -0
  266. src/vendor/php-http/message-factory/src/StreamFactory.php +25 -0
  267. src/vendor/php-http/message-factory/src/UriFactory.php +24 -0
  268. src/vendor/php-http/message/CHANGELOG.md +254 -0
  269. src/vendor/php-http/message/LICENSE +19 -0
  270. src/vendor/php-http/message/README.md +51 -0
  271. src/vendor/php-http/message/apigen.neon +6 -0
  272. src/vendor/php-http/message/composer.json +68 -0
  273. src/vendor/php-http/message/puli.json +96 -0
call-now-button.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Call Now Button
4
  Plugin URI: https://callnowbutton.com
5
  Description: Mobile visitors will see a <strong>Call Now Button</strong> on your website. Easy to use but flexible to meet more demanding requirements. Change placement and color, hide on specific pages, track how many people click them or conversions of your Google Ads campaigns. It's all optional but possible.
6
- Version: 1.1.8
7
  Author: Jerry Rietveld
8
  Author URI: https://www.callnowbutton.com
9
  GitHub Plugin URI: https://github.com/callnowbutton/wp-plugin
@@ -26,7 +26,7 @@ License: GPL2
26
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27
  */
28
 
29
- define('CNB_VERSION', '1.1.8');
30
  define('CNB_NAME', 'Call Now Button');
31
  define('CNB_BASENAME', plugin_basename(__FILE__));
32
  define('CNB_BASEFOLDER', plugin_basename(dirname(__FILE__)));
@@ -36,4 +36,6 @@ define('CNB_SUPPORT', CNB_WEBSITE . 'support/');
36
  define('CNB_APP', 'https://app.callnowbutton.com/');
37
  define('CNB_SLUG', sanitize_title(CNB_NAME));
38
 
 
 
39
  require_once dirname( __FILE__ ) . '/src/call-now-button.php';
3
  Plugin Name: Call Now Button
4
  Plugin URI: https://callnowbutton.com
5
  Description: Mobile visitors will see a <strong>Call Now Button</strong> on your website. Easy to use but flexible to meet more demanding requirements. Change placement and color, hide on specific pages, track how many people click them or conversions of your Google Ads campaigns. It's all optional but possible.
6
+ Version: 1.1.9
7
  Author: Jerry Rietveld
8
  Author URI: https://www.callnowbutton.com
9
  GitHub Plugin URI: https://github.com/callnowbutton/wp-plugin
26
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27
  */
28
 
29
+ define('CNB_VERSION', '1.1.9');
30
  define('CNB_NAME', 'Call Now Button');
31
  define('CNB_BASENAME', plugin_basename(__FILE__));
32
  define('CNB_BASEFOLDER', plugin_basename(dirname(__FILE__)));
36
  define('CNB_APP', 'https://app.callnowbutton.com/');
37
  define('CNB_SLUG', sanitize_title(CNB_NAME));
38
 
39
+ register_deactivation_hook( __FILE__, array('cnb\admin\deactivation\Deactivation', 'onDeactivation') );
40
+
41
  require_once dirname( __FILE__ ) . '/src/call-now-button.php';
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: call button, click to call, convert, call now button, contact button
5
  Requires at least: 3.9
6
  Requires PHP: 5.4
7
  Tested up to: 6.0
8
- Stable tag: 1.1.8
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -111,6 +111,12 @@ Yes, you can upgrade to Premium to enable tons of extra features. Checkout [call
111
 
112
 
113
  == Changelog ==
 
 
 
 
 
 
114
  = 1.1.8 =
115
  * Added missing media query for body padding when using Full Width button
116
 
5
  Requires at least: 3.9
6
  Requires PHP: 5.4
7
  Tested up to: 6.0
8
+ Stable tag: 1.1.9
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
111
 
112
 
113
  == Changelog ==
114
+ = 1.1.9 =
115
+ * Introduced exit surveys
116
+ * Error & usage reporting setting (off by default)
117
+ * Email subscription setting (Premium)
118
+ * Small fixes/improvements
119
+
120
  = 1.1.8 =
121
  * Added missing media query for body padding when using Full Width button
122
 
resources/js/call-now-button.js CHANGED
@@ -489,7 +489,7 @@ function cnb_hide_add_new_on_error() {
489
 
490
  function cnb_setup_pricing() {
491
  // Find the elements
492
- const elements = jQuery('.eur-per-month, .usd-per-month');
493
 
494
  // If there are elements, find the pricing (ajax call)
495
  if (elements.length) {
@@ -499,8 +499,15 @@ function cnb_setup_pricing() {
499
  jQuery.post(ajaxurl, data)
500
  .done((result) => {
501
  // Fix the elements
 
 
 
 
 
502
  jQuery('.eur-per-month').text(result['eur_per_month'])
503
  jQuery('.usd-per-month').text(result['usd_per_month'])
 
 
504
  })
505
  }
506
  }
489
 
490
  function cnb_setup_pricing() {
491
  // Find the elements
492
+ const elements = jQuery('.eur-per-month, .usd-per-month, .eur-discount, .usd-discount');
493
 
494
  // If there are elements, find the pricing (ajax call)
495
  if (elements.length) {
499
  jQuery.post(ajaxurl, data)
500
  .done((result) => {
501
  // Fix the elements
502
+ result['eur_per_month'] = parseFloat(result['eur_per_month']).toFixed(2)
503
+ result['usd_per_month'] = parseFloat(result['usd_per_month']).toFixed(2)
504
+ result['eur_discount'] = parseFloat(result['eur_discount']).toFixed(1)
505
+ result['usd_discount'] = parseFloat(result['usd_discount']).toFixed(1)
506
+
507
  jQuery('.eur-per-month').text(result['eur_per_month'])
508
  jQuery('.usd-per-month').text(result['usd_per_month'])
509
+ jQuery('.eur-discount').text(result['eur_discount'])
510
+ jQuery('.usd-discount').text(result['usd_discount'])
511
  })
512
  }
513
  }
resources/js/condition-edit.js CHANGED
@@ -1,25 +1,73 @@
1
  function cnb_show_condition_placeholder_action() {
2
- const optionSelected = jQuery('#cnb_condition_match_type').val();
3
- let placeholderText;
4
  if(optionSelected === 'SIMPLE') {
5
- placeholderText = '/blog/';
6
  } else if(optionSelected === 'EXACT') {
7
- placeholderText = 'https://www.example.com/sample-page/';
8
  } else if(optionSelected === 'SUBSTRING') {
9
- placeholderText = 'category/';
10
  } else if(optionSelected === 'REGEX') {
11
- placeholderText = '/(index|about)(\?id=[0-9]+)?$';
 
 
12
  }
13
- jQuery('#cnb_condition_match_value').attr('placeholder', placeholderText);
14
  }
 
15
  /**
16
  * Show an example condition in the form field for each of the match types
17
  */
18
  function cnb_show_condition_placeholder() {
19
- cnb_show_condition_placeholder_action();
20
  jQuery('#cnb_condition_match_type').on('change', function () {
21
- cnb_show_condition_placeholder_action();
22
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
 
25
  /**
@@ -29,43 +77,44 @@ function cnb_delete_condition() {
29
  jQuery('tbody[data-wp-lists="list:cnb_list_condition"]#the-list span.delete a[data-ajax="true"]')
30
  .on('click', function(){
31
  // Prep data
32
- const id = jQuery(this).data('id');
33
- const bid = jQuery(this).data('bid');
34
  const data = {
35
  'action': 'cnb_delete_condition',
36
  'id': id,
37
  'bid': bid,
38
  '_ajax_nonce': jQuery(this).data('wpnonce'),
39
- };
40
 
41
  // Send remove request
42
  jQuery.post(ajaxurl, data)
43
  .done(() => {
44
  // Remove container
45
- const action_row = jQuery(this).closest('tr');
46
- jQuery(action_row).css("background-color", "#ff726f");
47
  jQuery(action_row).fadeOut(function() {
48
- jQuery(action_row).css("background-color", "");
49
- jQuery(action_row).remove();
50
 
51
  // Special case: if this is the last item, show a "no items" row
52
- const remaining_items = jQuery('table.cnb_list_conditions #the-list tr').length;
53
  if (!remaining_items) {
54
  // Add row
55
  jQuery('table.cnb_list_conditions #the-list').html('<tr class="no-items"><td class="colspanchange" colspan="5"<p class="cnb_paragraph">You have no page visibility rules set up. This means that your button will show on all pages.</p>' +
56
- '<p class="cnb_paragraph">Click the <code>Add page rule</code> button above to limit the appearance. You can freely mix and match rules to meet your requirements.</p></td></tr>');
57
  }
58
- });
59
- });
60
 
61
  // Remove ID from Button array
62
- jQuery('input[name^="conditions['+id+']"').remove();
63
- return false;
64
- });
65
  }
66
 
67
 
68
  jQuery( function() {
69
- cnb_delete_condition();
70
- cnb_show_condition_placeholder();
 
71
  })
1
  function cnb_show_condition_placeholder_action() {
2
+ const optionSelected = jQuery('#cnb_condition_match_type').val()
3
+ let placeholderText
4
  if(optionSelected === 'SIMPLE') {
5
+ placeholderText = '/blog/'
6
  } else if(optionSelected === 'EXACT') {
7
+ placeholderText = 'https://www.example.com/sample-page/'
8
  } else if(optionSelected === 'SUBSTRING') {
9
+ placeholderText = 'category/'
10
  } else if(optionSelected === 'REGEX') {
11
+ placeholderText = '/(index|about)(\?id=[0-9]+)?$'
12
+ } else if(optionSelected === 'COUNTRY_CODE') {
13
+ placeholderText = 'NL'
14
  }
15
+ jQuery('#cnb_condition_match_value').attr('placeholder', placeholderText)
16
  }
17
+
18
  /**
19
  * Show an example condition in the form field for each of the match types
20
  */
21
  function cnb_show_condition_placeholder() {
22
+ cnb_show_condition_placeholder_action()
23
  jQuery('#cnb_condition_match_type').on('change', function () {
24
+ cnb_show_condition_placeholder_action()
25
+ })
26
+ }
27
+
28
+ /**
29
+ *
30
+ * @param {HTMLElement} element
31
+ */
32
+ function cnb_condition_type_change_action(element) {
33
+ // Get the select item
34
+ const selected = jQuery(element).find(":selected")
35
+ // Get the new option
36
+ const value = selected.val()
37
+
38
+ // Hide all "conditionType"
39
+ jQuery('.conditionType').attr('hidden', 'hidden')
40
+
41
+ // Show all "conditionType_TYPE"
42
+ jQuery('.conditionType_' + value).removeAttr('hidden')
43
+
44
+ // Ensure the selected item is NOT a hidden item
45
+ // If it is, select the first non-hiden version
46
+ const matchTypeEle = jQuery('#cnb_condition_match_type')
47
+ const selectedMatchType = matchTypeEle.find(":selected")
48
+
49
+ if (selectedMatchType.is('[hidden="hidden"]')) {
50
+ const firstNotHidden = matchTypeEle.find('option[hidden!="hidden"]')
51
+ if (firstNotHidden.length) {
52
+ firstNotHidden.first().attr('selected', 'selected')
53
+
54
+ // Also clear the Match value, since it's no longer valid
55
+ jQuery('#cnb_condition_match_value').val('')
56
+ cnb_show_condition_placeholder_action()
57
+ }
58
+ }
59
+ }
60
+
61
+ function cnb_condition_type_change_listener() {
62
+ const ele = jQuery('#cnb_condition_condition_type')
63
+ if (!ele.length) {
64
+ return
65
+ }
66
+
67
+ ele.on('change', function (element) {
68
+ cnb_condition_type_change_action(element.target)
69
+ })
70
+ cnb_condition_type_change_action(ele[0])
71
  }
72
 
73
  /**
77
  jQuery('tbody[data-wp-lists="list:cnb_list_condition"]#the-list span.delete a[data-ajax="true"]')
78
  .on('click', function(){
79
  // Prep data
80
+ const id = jQuery(this).data('id')
81
+ const bid = jQuery(this).data('bid')
82
  const data = {
83
  'action': 'cnb_delete_condition',
84
  'id': id,
85
  'bid': bid,
86
  '_ajax_nonce': jQuery(this).data('wpnonce'),
87
+ }
88
 
89
  // Send remove request
90
  jQuery.post(ajaxurl, data)
91
  .done(() => {
92
  // Remove container
93
+ const action_row = jQuery(this).closest('tr')
94
+ jQuery(action_row).css("background-color", "#ff726f")
95
  jQuery(action_row).fadeOut(function() {
96
+ jQuery(action_row).css("background-color", "")
97
+ jQuery(action_row).remove()
98
 
99
  // Special case: if this is the last item, show a "no items" row
100
+ const remaining_items = jQuery('table.cnb_list_conditions #the-list tr').length
101
  if (!remaining_items) {
102
  // Add row
103
  jQuery('table.cnb_list_conditions #the-list').html('<tr class="no-items"><td class="colspanchange" colspan="5"<p class="cnb_paragraph">You have no page visibility rules set up. This means that your button will show on all pages.</p>' +
104
+ '<p class="cnb_paragraph">Click the <code>Add page rule</code> button above to limit the appearance. You can freely mix and match rules to meet your requirements.</p></td></tr>')
105
  }
106
+ })
107
+ })
108
 
109
  // Remove ID from Button array
110
+ jQuery('input[name^="conditions['+id+']"').remove()
111
+ return false
112
+ })
113
  }
114
 
115
 
116
  jQuery( function() {
117
+ cnb_delete_condition()
118
+ cnb_show_condition_placeholder()
119
+ cnb_condition_type_change_listener()
120
  })
resources/js/deactivation.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const cnb_tally_deactivate_plugin_form_id = 'waQ6eb'
2
+
3
+ function cnb_add_deactivation_init() {
4
+ cnb_add_deactivation_popup_tally()
5
+ }
6
+
7
+ /**
8
+ * Called when the Call Now Button plugin's "Deactivate" link is clicked
9
+ */
10
+ function cnb_add_deactivation_popup_tally() {
11
+ const original_link = jQuery('#deactivate-call-now-button')
12
+ original_link.on('click', (event) => {
13
+ event.preventDefault()
14
+
15
+ const options = {
16
+ layout: 'modal',
17
+ width: 450,
18
+ hideTitle: 1,
19
+ emoji: {
20
+ text: '😮',
21
+ animation: 'none'
22
+ },
23
+ hiddenFields: {
24
+ wordPressUrl: window.location.href,
25
+ },
26
+ onClose: () => window.location = event.target.href,
27
+ onSubmit: () => {
28
+ setTimeout(() => {
29
+ window.location = event.target.href
30
+ }, 7000)
31
+ },
32
+ }
33
+ Tally.openPopup(cnb_tally_deactivate_plugin_form_id, options)
34
+ })
35
+ }
36
+
37
+ jQuery(() => {
38
+ cnb_add_deactivation_init()
39
+ })
resources/js/error-reporting.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Error reporting is optional and disabled by default.
3
+ *
4
+ * It needs to be enabled via Settings in order to take effect.
5
+ */
6
+
7
+ function cnb_capture_js_errors() {
8
+ cnb_sentry_add_to_head()
9
+ return cnb_sentry_wait()
10
+ }
11
+
12
+ function cnb_sentry_add_to_head() {
13
+ // <script src='https://js.sentry-cdn.com/c88ed2804458402cad2a13537dac603f.min.js' crossorigin="anonymous"></script>
14
+ const s = document.createElement("script")
15
+ s.type = "text/javascript"
16
+ s.async = true
17
+ s.defer = true
18
+ s.crossOrigin = "anonymous"
19
+ s.src = "https://js.sentry-cdn.com/c88ed2804458402cad2a13537dac603f.min.js"
20
+ jQuery("head").append(s)
21
+ }
22
+
23
+ function cnb_sentry_wait() {
24
+ const timeout = 10000 //10 seconds
25
+ const start = Date.now()
26
+ return new Promise(cnb_wait_for_sentry)
27
+
28
+ function cnb_wait_for_sentry(resolve, reject) {
29
+ if (window.Sentry && window.Sentry.init)
30
+ resolve(cnb_sentry_onload())
31
+ else if (timeout && (Date.now() - start) >= timeout)
32
+ reject(new Error("window.Sentry not found (after waiting for " + timeout + "ms)"))
33
+ else
34
+ setTimeout(cnb_wait_for_sentry.bind(this, resolve, reject), 30)
35
+ }
36
+ }
37
+
38
+ function cnb_sentry_onload() {
39
+ Sentry.onLoad(function () {
40
+ const data = jQuery('#cnb-data')
41
+ if (data.length) {
42
+
43
+ Sentry.init({
44
+ release: data.data('pluginVersion'),
45
+ environment: data.data('wordpressEnvironment'),
46
+ })
47
+
48
+ Sentry.setContext("WordPress", {
49
+ version: data.data('wordpressVersion'),
50
+ })
51
+ }
52
+ })
53
+ return true
54
+ }
55
+
56
+ jQuery( function() {
57
+ cnb_capture_js_errors()
58
+ .catch((e) => {
59
+ // Ignore
60
+ console.debug('Could not load Sentry, client side JS errors will not be sent', e)
61
+ })
62
+ })
resources/js/preview.js CHANGED
@@ -1,11 +1,11 @@
1
  function getCleanDomain() {
2
- return document.location.hostname;
3
  }
4
 
5
  function createButtonFromData(formData) {
6
- let domainType = 'FREE';
7
  if (formData && formData.domain) {
8
- domainType = formData.domain.type;
9
  }
10
 
11
  return {
@@ -48,16 +48,16 @@ function createButtonFromData(formData) {
48
  * NOTE that this does not work for "daysOfWeek", since the proper order of the array is lost.
49
  */
50
  function convertArrayToObject (array, key) {
51
- const initialValue = {};
52
  return array.reduce((obj, item) => {
53
  return {
54
  ...obj,
55
  [item[key]]: item,
56
- };
57
- }, initialValue);
58
  }
59
 
60
- function livePreview() {
61
  const parsedData = jQuery('.cnb-container').serializeAssoc()
62
 
63
  // Find a button via JS (instead of via a form)
@@ -79,20 +79,23 @@ function livePreview() {
79
  }
80
 
81
  // Find the correct static base URL for the Client CSS file
82
- parsedData.cnb_css_root = 'https://static.callnowbutton.com';
83
  if (typeof cnb_css_root !== 'undefined') {
84
  parsedData.cnb_css_root = cnb_css_root
85
  }
86
 
87
  // Ensure it is always visible
88
- parsedData.button.options.displayMode = 'ALWAYS';
 
 
 
89
 
90
  // Ensure all Actions are visible
91
  if (typeof cnb_actions !== 'undefined' && cnb_ignore_schedule) {
92
  cnb_actions = cnb_actions.map((item) => {
93
- item.schedule.showAlways = true;
94
- return item
95
- });
96
  }
97
 
98
  if (!cnb_ignore_schedule) {
@@ -100,7 +103,7 @@ function livePreview() {
100
  }
101
 
102
  // This ensures we keep the order in the table
103
- parsedData.actions_ordered = [];
104
  if (parsedData.actions) {
105
  parsedData.actions_ordered = Object.values(parsedData.actions).map((item) => item.id)
106
  }
@@ -110,8 +113,8 @@ function livePreview() {
110
  if (parsedData &&
111
  parsedData.actions
112
  && ((parsedData.actions[Object.keys(parsedData.actions)[0]]
113
- && parsedData.actions[Object.keys(parsedData.actions)[0]].actionType)
114
- || parsedData.actions.new)) {
115
  // Editing Multi & Full Button
116
  parsedData.actions = Object.assign(convertArrayToObject(cnb_actions, 'id'), parsedData.actions)
117
 
@@ -138,17 +141,17 @@ function livePreview() {
138
  daysOfWeek
139
  )
140
  return item
141
- });
142
  }
143
 
144
  // Fix: Force all booleans for schedule (and force daysOfWeek into array)
145
  if (!cnb_ignore_schedule && parsedData.action_id && parsedData.actions &&
146
  parsedData.actions[parsedData.action_id]) {
147
 
148
- const showAlways = parsedData.actions[parsedData.action_id].schedule.showAlways;
149
- parsedData.actions[parsedData.action_id].schedule.showAlways = showAlways !== "false";
150
 
151
- const daysOfWeek = [0,1,2,3,4,5,6]
152
  let newDaysOfWeek = []
153
  for (const day in daysOfWeek) {
154
  const ele = jQuery('#cnb_weekday_' + day)
@@ -160,12 +163,12 @@ function livePreview() {
160
  // Fix iconenabled (should be true/false instead of 0/1)
161
  if (parsedData.action_id && parsedData.actions &&
162
  parsedData.actions[parsedData.action_id]) {
163
- const iconEnabled = parsedData.actions[parsedData.action_id].iconEnabled;
164
- parsedData.actions[parsedData.action_id].iconEnabled = iconEnabled !== "0";
165
  }
166
 
167
  if (typeof cnb_domain !== 'undefined') {
168
- parsedData.domain = cnb_domain;
169
  }
170
 
171
  // Ensure WhatsApp/Signal works
@@ -173,8 +176,8 @@ function livePreview() {
173
  parsedData.actions[parsedData.action_id] &&
174
  (parsedData.actions[parsedData.action_id].actionType === 'WHATSAPP' ||
175
  parsedData.actions[parsedData.action_id].actionType === 'SIGNAL')) {
176
- const input = document.querySelector('#cnb_action_value_input_whatsapp');
177
- const iti = window.intlTelInputGlobals.getInstance(input);
178
  parsedData.actions[parsedData.action_id].actionValue = iti.getNumber()
179
  }
180
 
@@ -184,26 +187,26 @@ function livePreview() {
184
  jQuery('.cnb-multi.call-now-button').remove()
185
  jQuery('.cnb-message-modal').remove()
186
 
187
- const cnbData = createButtonFromData(parsedData);
188
  const previewContainer = jQuery('#cnb-button-preview')
189
  previewContainer.text('')
190
  if (typeof CNB !== 'undefined') {
191
  // pass "false" to ensure we do NOT add the client's native observers
192
- const result = CNB.render(cnbData, false);
193
 
194
  // If there is a modal, trigger it
195
  // The "parsedData.action_id" check is to ensure it does not expand on a FULL or MULTI overview page
196
  if (parsedData.action_id) {
197
  const whatsappButton = jQuery('.call-now-button a[data-action-type="WHATSAPP"]')
198
  if (whatsappButton.length > 0) {
199
- whatsappButton[0].dispatchEvent(new window.CustomEvent('toggle'));
200
  }
201
  }
202
 
203
  // If there is a Multibutton, expand it (test this AFTER the modal, so we only toggle if it isn't ALREADY expanded)
204
  const multiButton = jQuery('.cnb-multi.call-now-button:not(.cnb-expand) .cnb-floating-main')
205
  if (multiButton.length > 0) {
206
- multiButton[0].dispatchEvent(new window.CustomEvent('toggle'));
207
  }
208
 
209
  // Move the result into a new special div (if found)
@@ -238,9 +241,9 @@ function updateScheduler(day, hour, minute) {
238
  date.setSeconds(0)
239
 
240
  // Settings day is weird...
241
- const currentDay = date.getDay();
242
- const distance = day - currentDay;
243
- date.setDate(date.getDate() + distance);
244
 
245
  cnb_options.date = date.getTime()
246
 
@@ -265,29 +268,27 @@ function initPreviewDayAndTimeSelector() {
265
 
266
  function initButtonEdit() {
267
  jQuery(() => {
268
- const idElement = jQuery('form.cnb-container :input[name="button[id]"]');
269
  if (idElement.length > 0 && !idElement.val().trim()) {
270
- return false;
271
  }
272
 
273
  // Load the required dependencies and render the preview once
274
  // All refreshes happen inside
275
- formToJson();
276
  livePreview()
277
  jQuery("form.cnb-container :input").on('change input', function() {
278
  livePreview()
279
- });
280
  // No need to call "livePreview", this is done via the ".done()" handler on cnb_delete_action()
281
- // jQuery('form.cnb-container a[data-ajax="true"]').on('change input', function() {});
282
  })
283
  }
284
 
285
  jQuery(() => {
286
  // This enables the scheduler (which can be disabled on a per-screen basis)
287
- window.cnb_ignore_schedule = false;
288
 
289
  initButtonEdit()
290
-
291
- initPreviewDayAndTimeSelector();
292
-
293
  })
1
  function getCleanDomain() {
2
+ return document.location.hostname
3
  }
4
 
5
  function createButtonFromData(formData) {
6
+ let domainType = 'FREE'
7
  if (formData && formData.domain) {
8
+ domainType = formData.domain.type
9
  }
10
 
11
  return {
48
  * NOTE that this does not work for "daysOfWeek", since the proper order of the array is lost.
49
  */
50
  function convertArrayToObject (array, key) {
51
+ const initialValue = {}
52
  return array.reduce((obj, item) => {
53
  return {
54
  ...obj,
55
  [item[key]]: item,
56
+ }
57
+ }, initialValue)
58
  }
59
 
60
+ async function livePreview() {
61
  const parsedData = jQuery('.cnb-container').serializeAssoc()
62
 
63
  // Find a button via JS (instead of via a form)
79
  }
80
 
81
  // Find the correct static base URL for the Client CSS file
82
+ parsedData.cnb_css_root = 'https://static.callnowbutton.com'
83
  if (typeof cnb_css_root !== 'undefined') {
84
  parsedData.cnb_css_root = cnb_css_root
85
  }
86
 
87
  // Ensure it is always visible
88
+ parsedData.button.options.displayMode = 'ALWAYS'
89
+
90
+ // This ensures that the scroll option(s) do not affect the preview
91
+ delete parsedData.button.options.scroll
92
 
93
  // Ensure all Actions are visible
94
  if (typeof cnb_actions !== 'undefined' && cnb_ignore_schedule) {
95
  cnb_actions = cnb_actions.map((item) => {
96
+ item.schedule.showAlways = true
97
+ return item
98
+ })
99
  }
100
 
101
  if (!cnb_ignore_schedule) {
103
  }
104
 
105
  // This ensures we keep the order in the table
106
+ parsedData.actions_ordered = []
107
  if (parsedData.actions) {
108
  parsedData.actions_ordered = Object.values(parsedData.actions).map((item) => item.id)
109
  }
113
  if (parsedData &&
114
  parsedData.actions
115
  && ((parsedData.actions[Object.keys(parsedData.actions)[0]]
116
+ && parsedData.actions[Object.keys(parsedData.actions)[0]].actionType)
117
+ || parsedData.actions.new)) {
118
  // Editing Multi & Full Button
119
  parsedData.actions = Object.assign(convertArrayToObject(cnb_actions, 'id'), parsedData.actions)
120
 
141
  daysOfWeek
142
  )
143
  return item
144
+ })
145
  }
146
 
147
  // Fix: Force all booleans for schedule (and force daysOfWeek into array)
148
  if (!cnb_ignore_schedule && parsedData.action_id && parsedData.actions &&
149
  parsedData.actions[parsedData.action_id]) {
150
 
151
+ const showAlways = parsedData.actions[parsedData.action_id].schedule.showAlways
152
+ parsedData.actions[parsedData.action_id].schedule.showAlways = showAlways !== "false"
153
 
154
+ const daysOfWeek = [0, 1, 2, 3, 4, 5, 6]
155
  let newDaysOfWeek = []
156
  for (const day in daysOfWeek) {
157
  const ele = jQuery('#cnb_weekday_' + day)
163
  // Fix iconenabled (should be true/false instead of 0/1)
164
  if (parsedData.action_id && parsedData.actions &&
165
  parsedData.actions[parsedData.action_id]) {
166
+ const iconEnabled = parsedData.actions[parsedData.action_id].iconEnabled
167
+ parsedData.actions[parsedData.action_id].iconEnabled = iconEnabled !== "0"
168
  }
169
 
170
  if (typeof cnb_domain !== 'undefined') {
171
+ parsedData.domain = cnb_domain
172
  }
173
 
174
  // Ensure WhatsApp/Signal works
176
  parsedData.actions[parsedData.action_id] &&
177
  (parsedData.actions[parsedData.action_id].actionType === 'WHATSAPP' ||
178
  parsedData.actions[parsedData.action_id].actionType === 'SIGNAL')) {
179
+ const input = document.querySelector('#cnb_action_value_input_whatsapp')
180
+ const iti = window.intlTelInputGlobals.getInstance(input)
181
  parsedData.actions[parsedData.action_id].actionValue = iti.getNumber()
182
  }
183
 
187
  jQuery('.cnb-multi.call-now-button').remove()
188
  jQuery('.cnb-message-modal').remove()
189
 
190
+ const cnbData = createButtonFromData(parsedData)
191
  const previewContainer = jQuery('#cnb-button-preview')
192
  previewContainer.text('')
193
  if (typeof CNB !== 'undefined') {
194
  // pass "false" to ensure we do NOT add the client's native observers
195
+ const result = await CNB.render(cnbData, false)
196
 
197
  // If there is a modal, trigger it
198
  // The "parsedData.action_id" check is to ensure it does not expand on a FULL or MULTI overview page
199
  if (parsedData.action_id) {
200
  const whatsappButton = jQuery('.call-now-button a[data-action-type="WHATSAPP"]')
201
  if (whatsappButton.length > 0) {
202
+ whatsappButton[0].dispatchEvent(new window.CustomEvent('toggle'))
203
  }
204
  }
205
 
206
  // If there is a Multibutton, expand it (test this AFTER the modal, so we only toggle if it isn't ALREADY expanded)
207
  const multiButton = jQuery('.cnb-multi.call-now-button:not(.cnb-expand) .cnb-floating-main')
208
  if (multiButton.length > 0) {
209
+ multiButton[0].dispatchEvent(new window.CustomEvent('toggle'))
210
  }
211
 
212
  // Move the result into a new special div (if found)
241
  date.setSeconds(0)
242
 
243
  // Settings day is weird...
244
+ const currentDay = date.getDay()
245
+ const distance = day - currentDay
246
+ date.setDate(date.getDate() + distance)
247
 
248
  cnb_options.date = date.getTime()
249
 
268
 
269
  function initButtonEdit() {
270
  jQuery(() => {
271
+ const idElement = jQuery('form.cnb-container :input[name="button[id]"]')
272
  if (idElement.length > 0 && !idElement.val().trim()) {
273
+ return false
274
  }
275
 
276
  // Load the required dependencies and render the preview once
277
  // All refreshes happen inside
278
+ formToJson()
279
  livePreview()
280
  jQuery("form.cnb-container :input").on('change input', function() {
281
  livePreview()
282
+ })
283
  // No need to call "livePreview", this is done via the ".done()" handler on cnb_delete_action()
284
+ // jQuery('form.cnb-container a[data-ajax="true"]').on('change input', function() {})
285
  })
286
  }
287
 
288
  jQuery(() => {
289
  // This enables the scheduler (which can be disabled on a per-screen basis)
290
+ window.cnb_ignore_schedule = false
291
 
292
  initButtonEdit()
293
+ initPreviewDayAndTimeSelector()
 
 
294
  })
resources/js/settings.js CHANGED
@@ -115,6 +115,36 @@ function cnb_delete_apikey() {
115
  return false;
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  /**
119
  * Disable the Cloud inputs when it is disabled (but only on the settings screen,
120
  * where that checkbox is actually visible)
@@ -132,5 +162,6 @@ function init_settings() {
132
 
133
  jQuery(() => {
134
  init_settings();
135
- cnb_disable_api_key_when_cloud_hosting_is_disabled();
 
136
  })
115
  return false;
116
  }
117
 
118
+ const cnb_tally_deactivate_premium_form_id = 'wA7d2z'
119
+
120
+ function cnb_ask_for_feedback_disable_cloud() {
121
+ const ele = jQuery('#cnb_cloud_enabled')
122
+ const isChecked = ele.is(':checked')
123
+ if (isChecked) {
124
+ ele.on('click', () => {
125
+ const isChecked = ele.is(':checked')
126
+ const options = {
127
+ width: 450,
128
+ hideTitle: 1,
129
+ emoji: {
130
+ text: '😢',
131
+ animation: 'heart-beat'
132
+ },
133
+ autoClose: 5000,
134
+ hiddenFields: {
135
+ wordPressUrl: window.location.href,
136
+ userId: jQuery('#cnb_user_id').text()
137
+ }
138
+ }
139
+ if (!isChecked) {
140
+ Tally.openPopup(cnb_tally_deactivate_premium_form_id, options)
141
+ } else {
142
+ Tally.closePopup(cnb_tally_deactivate_premium_form_id)
143
+ }
144
+ })
145
+ }
146
+ }
147
+
148
  /**
149
  * Disable the Cloud inputs when it is disabled (but only on the settings screen,
150
  * where that checkbox is actually visible)
162
 
163
  jQuery(() => {
164
  init_settings();
165
+ cnb_disable_api_key_when_cloud_hosting_is_disabled()
166
+ cnb_ask_for_feedback_disable_cloud()
167
  })
src/CallNowButton.php CHANGED
@@ -5,10 +5,27 @@ namespace cnb;
5
  // don't load directly
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
 
 
8
  use cnb\admin\api\CnbAppRemote;
 
 
 
 
 
 
 
 
 
 
9
  use cnb\admin\legacy\CnbLegacyController;
 
 
 
 
10
  use cnb\admin\settings\CnbSettingsController;
11
- use cnb\utils\CnbAdminFunctions;
 
12
  use cnb\utils\CnbUtils;
13
 
14
  class CallNowButton {
@@ -16,7 +33,7 @@ class CallNowButton {
16
  /**
17
  * Adds the plugin to the options menu
18
  */
19
- public static function register_admin_pages() {
20
  global $wp_version;
21
 
22
  $cnb_options = get_option( 'cnb' );
@@ -24,9 +41,11 @@ class CallNowButton {
24
  $cnb_cloud_hosting = $utils->isCloudActive( $cnb_options );
25
  $plugin_title = apply_filters( 'cnb_plugin_title', CNB_NAME );
26
 
 
 
27
  $menu_page_function = $cnb_cloud_hosting ?
28
- array( 'cnb\admin\button\CnbButtonRouter', 'render' ) :
29
- array( 'cnb\admin\legacy\CnbLegacyEdit', 'render' );
30
 
31
  $counter = 0;
32
  $menu_page_title = 'Call Now Button<span class="awaiting-mod" id="cnb-nav-counter" style="display: none">' . $counter . '</span>';
@@ -62,90 +81,58 @@ class CallNowButton {
62
 
63
  if ( $cnb_cloud_hosting ) {
64
  // Button overview
65
- add_submenu_page( CNB_SLUG, $plugin_title, 'All buttons', 'manage_options', CNB_SLUG, array(
66
- 'cnb\admin\button\CnbButtonRouter',
67
- 'render'
68
- ) );
69
 
70
- add_submenu_page( CNB_SLUG, $plugin_title, 'Add New', 'manage_options', CNB_SLUG . '&action=new', array(
71
- 'cnb\admin\button\CnbButtonRouter',
72
- 'render'
73
- ) );
74
 
 
 
 
 
 
75
  if ( $cnb_options['advanced_view'] === 1 ) {
76
  // Domain overview
77
- add_submenu_page( CNB_SLUG, $plugin_title, 'Domains', 'manage_options', CNB_SLUG . '-domains', array(
78
- 'cnb\admin\domain\CnbDomainRouter',
79
- 'render'
80
- ) );
81
 
82
  // Action overview
83
- add_submenu_page( CNB_SLUG, $plugin_title, 'Actions', 'manage_options', CNB_SLUG . '-actions', array(
84
- 'cnb\admin\action\CnbActionRouter',
85
- 'render'
86
- ) );
87
 
88
  // Condition overview
89
- add_submenu_page( CNB_SLUG, $plugin_title, 'Conditions', 'manage_options', CNB_SLUG . '-conditions', array(
90
- 'cnb\admin\condition\CnbConditionRouter',
91
- 'render'
92
- ) );
93
 
94
  // Apikey overview
95
- add_submenu_page( CNB_SLUG, $plugin_title, 'API Keys', 'manage_options', CNB_SLUG . '-apikeys', array(
96
- 'cnb\admin\apikey\CnbApiKeyRouter',
97
- 'render'
98
- ) );
99
 
100
  // Profile edit
101
- add_submenu_page( CNB_SLUG, $plugin_title, 'Profile', 'manage_options', CNB_SLUG . '-profile', array(
102
- 'cnb\admin\profile\CnbProfileEdit',
103
- 'render'
104
- ) );
105
  } else {
106
  // Fake out Action overview
107
  if ( $utils->get_query_val( 'page' ) === 'call-now-button-actions' && $utils->get_query_val( 'action' ) ) {
108
- add_submenu_page( CNB_SLUG, $plugin_title, 'Edit action', 'manage_options', CNB_SLUG . '-actions', array(
109
- 'cnb\admin\action\CnbActionRouter',
110
- 'render'
111
- ) );
112
  }
113
  // Fake out Conditions overview
114
  if ( $utils->get_query_val( 'page' ) === 'call-now-button-conditions' && $utils->get_query_val( 'action' ) ) {
115
- add_submenu_page( CNB_SLUG, $plugin_title, 'Edit condition', 'manage_options', CNB_SLUG . '-conditions', array(
116
- 'cnb\admin\condition\CnbConditionRouter',
117
- 'render'
118
- ) );
119
  }
120
  // Fake out Domain upgrade page
121
  if ( $utils->get_query_val( 'page' ) === 'call-now-button-domains' && $utils->get_query_val( 'action' ) === 'upgrade' ) {
122
- add_submenu_page( CNB_SLUG, $plugin_title, 'Upgrade domain', 'manage_options', CNB_SLUG . '-domains', array(
123
- 'cnb\admin\domain\CnbDomainRouter',
124
- 'render'
125
- ) );
126
  }
127
  }
128
  } else {
129
  // Legacy edit
130
- add_submenu_page( CNB_SLUG, $plugin_title, 'My button', 'manage_options', CNB_SLUG, array(
131
- 'cnb\admin\legacy\CnbLegacyEdit',
132
- 'render'
133
- ) );
134
 
135
- add_submenu_page( CNB_SLUG, $plugin_title, 'Unlock features', 'manage_options', CNB_SLUG . '-upgrade', array(
136
- 'cnb\admin\legacy\CnbLegacyUpgrade',
137
- 'render'
138
- ) );
139
  }
140
 
141
  // Settings pages
142
- add_submenu_page( CNB_SLUG, $plugin_title, 'Settings', 'manage_options', CNB_SLUG . '-settings', array(
143
- 'cnb\admin\settings\CnbSettingsRouter',
144
- 'render'
145
- ) );
146
  }
147
 
148
- public static function plugin_meta( $links, $file ) {
149
  $cnb_options = get_option( 'cnb' );
150
  $cnb_utils = new CnbUtils();
151
  $cnb_cloud_hosting = $cnb_utils->isCloudActive( $cnb_options );
@@ -185,7 +172,7 @@ class CallNowButton {
185
  return $links;
186
  }
187
 
188
- public static function plugin_add_action_link( $links ) {
189
  $cnb_options = get_option( 'cnb' );
190
  $cnb_cloud_hosting = ( new CnbUtils() )->isCloudActive( $cnb_options );
191
 
@@ -199,7 +186,7 @@ class CallNowButton {
199
  $url );
200
  $button_url = esc_url( $button_link );
201
  $button = sprintf( '<a href="%s">%s</a>', $button_url, $link_name );
202
- array_unshift( $links, $button );
203
 
204
  if ( ! $cnb_cloud_hosting ) {
205
  $link_name = 'Get Premium';
@@ -217,24 +204,29 @@ class CallNowButton {
217
  return $links;
218
  }
219
 
220
- public static function options_init() {
221
  // This ensures that we can validate and change/manipulate the "cnb" options before saving
 
222
  register_setting(
223
  'cnb_options',
224
  'cnb',
225
  array(
226
  'type' => 'array',
227
  'description' => 'Settings for the Legacy and Cloud version of the Call Now Button',
228
- 'sanitize_callback' => array( 'cnb\admin\settings\CnbSettingsController', 'validate_options' ),
229
- 'default' => CnbSettingsController::get_defaults()
230
  ) );
231
  }
232
 
233
- public static function unregister_options() {
 
 
 
 
234
  unregister_setting( 'cnb_options', 'cnb' );
235
  }
236
 
237
- public static function register_styles_and_scripts() {
238
  wp_register_style(
239
  CNB_SLUG . '-styling',
240
  plugins_url( '../resources/style/call-now-button.css', __FILE__ ),
@@ -355,6 +347,24 @@ class CallNowButton {
355
  array( CNB_SLUG . '-call-now-button' ),
356
  CNB_VERSION,
357
  true );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
 
359
  // Special case: since the preview functionality depends on this,
360
  // and the source is always changing - we include it as external script
@@ -374,93 +384,90 @@ class CallNowButton {
374
  true );
375
  }
376
 
377
- public static function registerGlobalActions() {
378
- add_action( 'admin_menu', array( 'cnb\CallNowButton', 'register_admin_pages' ) );
379
- add_filter( 'plugin_row_meta', array( 'cnb\CallNowButton', 'plugin_meta' ), 10, 2 );
380
- add_filter( 'plugin_action_links_' . CNB_BASENAME, array( 'cnb\CallNowButton', 'plugin_add_action_link' ) );
381
 
382
- add_action( 'admin_init', array( 'cnb\CallNowButton', 'options_init' ) );
383
- add_action( 'admin_init', array( 'cnb\CallNowButton', 'register_styles_and_scripts' ) );
384
- add_filter( 'option_cnb', array( 'cnb\admin\settings\CnbSettingsController', 'post_option_cnb' ) );
 
 
 
 
 
385
 
386
  // This updates the internal version number, called by CnbAdminNotices::action_admin_init
387
- add_action( 'cnb_update_' . CNB_VERSION, array( 'cnb\utils\CnbUtils', 'update_version' ) );
 
 
 
 
388
  }
389
 
390
- public static function registerHeaderAndFooter() {
391
  // Generic header/footer
392
- add_action( 'cnb_header', array( 'cnb\CnbHeader', 'render' ) );
393
- add_action( 'cnb_footer', array( 'cnb\CnbFooter', 'render' ) );
 
 
394
  }
395
 
396
  /**
397
  * Page specific actions
398
  * @return void
399
  */
400
- public static function registerPostActions() {
401
- add_action( 'admin_post_cnb_create_single_button', array( 'cnb\admin\button\CnbButtonController', 'create' ) );
402
- add_action( 'admin_post_cnb_create_multi_button', array( 'cnb\admin\button\CnbButtonController', 'create' ) );
403
- add_action( 'admin_post_cnb_create_full_button', array( 'cnb\admin\button\CnbButtonController', 'create' ) );
404
-
405
- add_action( 'admin_post_cnb_update_single_button', array( 'cnb\admin\button\CnbButtonController', 'update' ) );
406
- add_action( 'admin_post_cnb_update_multi_button', array( 'cnb\admin\button\CnbButtonController', 'update' ) );
407
- add_action( 'admin_post_cnb_update_full_button', array( 'cnb\admin\button\CnbButtonController', 'update' ) );
408
-
409
- add_action( 'admin_post_cnb_buttons_bulk', array(
410
- 'cnb\admin\button\CnbButtonController',
411
- 'handle_bulk_actions'
412
- ) );
413
-
414
- add_action( 'admin_post_cnb_apikey_create', array( 'cnb\admin\apikey\CnbApiKeyController', 'create' ) );
415
- add_action( 'admin_post_cnb_apikey_bulk', array(
416
- 'cnb\admin\apikey\CnbApiKeyController',
417
- 'handle_bulk_actions'
418
- ) );
419
-
420
- add_action( 'admin_post_cnb_create_condition', array(
421
- 'cnb\admin\condition\CnbConditionController',
422
- 'create'
423
- ) );
424
- add_action( 'admin_post_cnb_update_condition', array(
425
- 'cnb\admin\condition\CnbConditionController',
426
- 'update'
427
- ) );
428
- add_action( 'admin_post_cnb_conditions_bulk', array(
429
- 'cnb\admin\condition\CnbConditionController',
430
- 'handle_bulk_actions'
431
- ) );
432
-
433
- add_action( 'admin_post_cnb_create_action', array( 'cnb\admin\action\CnbActionController', 'create' ) );
434
- add_action( 'admin_post_cnb_update_action', array( 'cnb\admin\action\CnbActionController', 'update' ) );
435
- add_action( 'admin_post_cnb_actions_bulk', array(
436
- 'cnb\admin\action\CnbActionController',
437
- 'handle_bulk_actions'
438
- ) );
439
-
440
- add_action( 'admin_post_cnb_create_domain', array( 'cnb\admin\domain\CnbDomainController', 'create' ) );
441
- add_action( 'admin_post_cnb_update_domain', array( 'cnb\admin\domain\CnbDomainController', 'update' ) );
442
- add_action( 'admin_post_cnb_domains_bulk', array(
443
- 'cnb\admin\domain\CnbDomainController',
444
- 'handle_bulk_actions'
445
- ) );
446
-
447
- add_action( 'admin_post_cnb_profile_edit', array( 'cnb\admin\profile\CnbProfileController', 'update' ) );
448
  }
449
 
450
- public static function registerAjax() {
451
- add_action( 'wp_ajax_cnb_time_format', array( 'cnb\admin\CnbAdminAjax', 'time_format' ) );
452
- add_action( 'wp_ajax_cnb_settings_profile_save', array( 'cnb\admin\CnbAdminAjax', 'settings_profile_save' ) );
453
- add_action( 'wp_ajax_cnb_delete_action', array( 'cnb\admin\action\CnbActionController', 'delete_ajax' ) );
454
- add_action( 'wp_ajax_cnb_delete_condition', array(
455
- 'cnb\admin\condition\CnbConditionController',
456
- 'delete_ajax'
457
- ) );
458
- add_action( 'wp_ajax_cnb_get_checkout', array( 'cnb\admin\CnbAdminAjax', 'domain_upgrade_get_checkout' ) );
459
- add_action( 'wp_ajax_cnb_email_activation', array( 'cnb\admin\CnbAdminAjax', 'cnb_email_activation' ) );
460
- add_action( 'wp_ajax_cnb_domain_timezone_change', array(
461
- 'cnb\admin\domain\CnbDomainController',
462
- 'updateTimezone'
463
- ) );
464
- add_action( 'wp_ajax_cnb_get_plans', array( 'cnb\admin\CnbAdminAjax', 'get_plans' ) );
 
465
  }
466
  }
5
  // don't load directly
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
8
+ use cnb\admin\action\CnbActionController;
9
+ use cnb\admin\action\CnbActionRouter;
10
  use cnb\admin\api\CnbAppRemote;
11
+ use cnb\admin\apikey\CnbApiKeyController;
12
+ use cnb\admin\apikey\CnbApiKeyRouter;
13
+ use cnb\admin\button\CnbButtonController;
14
+ use cnb\admin\button\CnbButtonRouter;
15
+ use cnb\admin\CnbAdminAjax;
16
+ use cnb\admin\condition\CnbConditionController;
17
+ use cnb\admin\condition\CnbConditionRouter;
18
+ use cnb\admin\deactivation\Deactivation;
19
+ use cnb\admin\domain\CnbDomainController;
20
+ use cnb\admin\domain\CnbDomainRouter;
21
  use cnb\admin\legacy\CnbLegacyController;
22
+ use cnb\admin\legacy\CnbLegacyEdit;
23
+ use cnb\admin\legacy\CnbLegacyUpgrade;
24
+ use cnb\admin\profile\CnbProfileController;
25
+ use cnb\admin\profile\CnbProfileRouter;
26
  use cnb\admin\settings\CnbSettingsController;
27
+ use cnb\admin\settings\CnbSettingsRouter;
28
+ use cnb\utils\Cnb_Sentry;
29
  use cnb\utils\CnbUtils;
30
 
31
  class CallNowButton {
33
  /**
34
  * Adds the plugin to the options menu
35
  */
36
+ public function register_admin_pages() {
37
  global $wp_version;
38
 
39
  $cnb_options = get_option( 'cnb' );
41
  $cnb_cloud_hosting = $utils->isCloudActive( $cnb_options );
42
  $plugin_title = apply_filters( 'cnb_plugin_title', CNB_NAME );
43
 
44
+ $button_router = new CnbButtonRouter();
45
+ $legacy_edit = new CnbLegacyEdit();
46
  $menu_page_function = $cnb_cloud_hosting ?
47
+ array( $button_router, 'render' ) :
48
+ array( $legacy_edit, 'render' );
49
 
50
  $counter = 0;
51
  $menu_page_title = 'Call Now Button<span class="awaiting-mod" id="cnb-nav-counter" style="display: none">' . $counter . '</span>';
81
 
82
  if ( $cnb_cloud_hosting ) {
83
  // Button overview
84
+ add_submenu_page( CNB_SLUG, $plugin_title, 'All buttons', 'manage_options', CNB_SLUG, array( $button_router, 'render' ) );
 
 
 
85
 
86
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Add New', 'manage_options', CNB_SLUG . '&action=new', array( $button_router, 'render' ) );
 
 
 
87
 
88
+ $domain_router = new CnbDomainRouter();
89
+ $action_router = new CnbActionRouter();
90
+ $condition_router = new CnbConditionRouter();
91
+ $api_key_router = new CnbApiKeyRouter();
92
+ $profile_router = new CnbProfileRouter();
93
  if ( $cnb_options['advanced_view'] === 1 ) {
94
  // Domain overview
95
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Domains', 'manage_options', CNB_SLUG . '-domains', array( $domain_router, 'render' ) );
 
 
 
96
 
97
  // Action overview
98
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Actions', 'manage_options', CNB_SLUG . '-actions', array( $action_router, 'render' ) );
 
 
 
99
 
100
  // Condition overview
101
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Conditions', 'manage_options', CNB_SLUG . '-conditions', array( $condition_router, 'render' ) );
 
 
 
102
 
103
  // Apikey overview
104
+ add_submenu_page( CNB_SLUG, $plugin_title, 'API Keys', 'manage_options', CNB_SLUG . '-apikeys', array( $api_key_router, 'render' ) );
 
 
 
105
 
106
  // Profile edit
107
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Profile', 'manage_options', CNB_SLUG . '-profile', array( $profile_router, 'render' ) );
 
 
 
108
  } else {
109
  // Fake out Action overview
110
  if ( $utils->get_query_val( 'page' ) === 'call-now-button-actions' && $utils->get_query_val( 'action' ) ) {
111
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Edit action', 'manage_options', CNB_SLUG . '-actions', array( $action_router, 'render' ) );
 
 
 
112
  }
113
  // Fake out Conditions overview
114
  if ( $utils->get_query_val( 'page' ) === 'call-now-button-conditions' && $utils->get_query_val( 'action' ) ) {
115
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Edit condition', 'manage_options', CNB_SLUG . '-conditions', array( $condition_router, 'render' ) );
 
 
 
116
  }
117
  // Fake out Domain upgrade page
118
  if ( $utils->get_query_val( 'page' ) === 'call-now-button-domains' && $utils->get_query_val( 'action' ) === 'upgrade' ) {
119
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Upgrade domain', 'manage_options', CNB_SLUG . '-domains', array( $domain_router, 'render' ) );
 
 
 
120
  }
121
  }
122
  } else {
123
  // Legacy edit
124
+ add_submenu_page( CNB_SLUG, $plugin_title, 'My button', 'manage_options', CNB_SLUG, array( $legacy_edit, 'render' ) );
 
 
 
125
 
126
+ $legacy_upgrade =new CnbLegacyUpgrade();
127
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Unlock features', 'manage_options', CNB_SLUG . '-upgrade', array( $legacy_upgrade, 'render' ) );
 
 
128
  }
129
 
130
  // Settings pages
131
+ $settings_router = new CnbSettingsRouter();
132
+ add_submenu_page( CNB_SLUG, $plugin_title, 'Settings', 'manage_options', CNB_SLUG . '-settings', array( $settings_router, 'render' ) );
 
 
133
  }
134
 
135
+ public function plugin_meta( $links, $file ) {
136
  $cnb_options = get_option( 'cnb' );
137
  $cnb_utils = new CnbUtils();
138
  $cnb_cloud_hosting = $cnb_utils->isCloudActive( $cnb_options );
172
  return $links;
173
  }
174
 
175
+ public function plugin_add_action_link( $links ) {
176
  $cnb_options = get_option( 'cnb' );
177
  $cnb_cloud_hosting = ( new CnbUtils() )->isCloudActive( $cnb_options );
178
 
186
  $url );
187
  $button_url = esc_url( $button_link );
188
  $button = sprintf( '<a href="%s">%s</a>', $button_url, $link_name );
189
+ $links['cnb_buttons'] = $button;
190
 
191
  if ( ! $cnb_cloud_hosting ) {
192
  $link_name = 'Get Premium';
204
  return $links;
205
  }
206
 
207
+ public function options_init() {
208
  // This ensures that we can validate and change/manipulate the "cnb" options before saving
209
+ $settings_controller = new CnbSettingsController();
210
  register_setting(
211
  'cnb_options',
212
  'cnb',
213
  array(
214
  'type' => 'array',
215
  'description' => 'Settings for the Legacy and Cloud version of the Call Now Button',
216
+ 'sanitize_callback' => array( $settings_controller, 'validate_options' ),
217
+ 'default' => $settings_controller->get_defaults()
218
  ) );
219
  }
220
 
221
+ /**
222
+ * Only used by tests
223
+ * @return void
224
+ */
225
+ public function unregister_options() {
226
  unregister_setting( 'cnb_options', 'cnb' );
227
  }
228
 
229
+ public function register_styles_and_scripts() {
230
  wp_register_style(
231
  CNB_SLUG . '-styling',
232
  plugins_url( '../resources/style/call-now-button.css', __FILE__ ),
347
  array( CNB_SLUG . '-call-now-button' ),
348
  CNB_VERSION,
349
  true );
350
+ wp_register_script(
351
+ CNB_SLUG . '-deactivation',
352
+ plugins_url( '../resources/js/deactivation.js', __FILE__ ),
353
+ array( CNB_SLUG . '-tally' ),
354
+ CNB_VERSION,
355
+ true );
356
+ wp_register_script(
357
+ CNB_SLUG . '-error-reporting',
358
+ plugins_url( '../resources/js/error-reporting.js', __FILE__ ),
359
+ array(),
360
+ CNB_VERSION,
361
+ true );
362
+ wp_register_script(
363
+ CNB_SLUG . '-tally',
364
+ 'https://tally.so/widgets/embed.js',
365
+ array(),
366
+ CNB_VERSION,
367
+ true );
368
 
369
  // Special case: since the preview functionality depends on this,
370
  // and the source is always changing - we include it as external script
384
  true );
385
  }
386
 
387
+ public function register_global_actions() {
388
+ add_action( 'admin_menu', array( $this, 'register_admin_pages' ) );
389
+ add_filter( 'plugin_row_meta', array( $this, 'plugin_meta' ), 10, 2 );
390
+ add_filter( 'plugin_action_links_' . CNB_BASENAME, array( $this, 'plugin_add_action_link' ) );
391
 
392
+ add_action( 'admin_init', array( $this, 'options_init' ) );
393
+ add_action( 'admin_init', array( $this, 'register_styles_and_scripts' ) );
394
+
395
+ $deactivation = new Deactivation();
396
+ add_action( 'admin_init', array($deactivation, 'register_deactivation_popup' ) );
397
+
398
+ $settings_controller = new CnbSettingsController();
399
+ add_filter( 'option_cnb', array( $settings_controller, 'post_option_cnb' ) );
400
 
401
  // This updates the internal version number, called by CnbAdminNotices::action_admin_init
402
+ add_action( 'cnb_update_' . CNB_VERSION, array( $settings_controller, 'update_version' ) );
403
+
404
+ $cnbSentry = new Cnb_Sentry();
405
+ add_action('cnb_init', array($cnbSentry, 'init'), 10, 2);
406
+ add_action('cnb_finish', array($cnbSentry, 'finish'));
407
  }
408
 
409
+ public function register_header_and_footer() {
410
  // Generic header/footer
411
+ $header = new CnbHeader();
412
+ add_action( 'cnb_header', array( $header, 'render' ) );
413
+ $footer = new CnbFooter();
414
+ add_action( 'cnb_footer', array( $footer, 'render' ) );
415
  }
416
 
417
  /**
418
  * Page specific actions
419
  * @return void
420
  */
421
+ public function register_admin_post_actions() {
422
+ $button_controller = new CnbButtonController();
423
+ add_action( 'admin_post_cnb_create_single_button', array( $button_controller, 'create' ) );
424
+ add_action( 'admin_post_cnb_create_multi_button', array( $button_controller, 'create' ) );
425
+ add_action( 'admin_post_cnb_create_full_button', array( $button_controller, 'create' ) );
426
+
427
+ add_action( 'admin_post_cnb_update_single_button', array( $button_controller, 'update' ) );
428
+ add_action( 'admin_post_cnb_update_multi_button', array( $button_controller, 'update' ) );
429
+ add_action( 'admin_post_cnb_update_full_button', array( $button_controller, 'update' ) );
430
+
431
+ add_action( 'admin_post_cnb_buttons_bulk', array( $button_controller, 'handle_bulk_actions' ) );
432
+
433
+ $api_key_controller = new CnbApiKeyController();
434
+ add_action( 'admin_post_cnb_apikey_create', array( $api_key_controller, 'create' ) );
435
+ add_action( 'admin_post_cnb_apikey_bulk', array( $api_key_controller, 'handle_bulk_actions' ) );
436
+
437
+ $condition_controller = new CnbConditionController();
438
+ add_action( 'admin_post_cnb_create_condition', array( $condition_controller, 'create' ) );
439
+ add_action( 'admin_post_cnb_update_condition', array( $condition_controller, 'update' ) );
440
+ add_action( 'admin_post_cnb_conditions_bulk', array( $condition_controller, 'handle_bulk_actions' ) );
441
+
442
+ $action_controller = new CnbActionController();
443
+ add_action( 'admin_post_cnb_create_action', array( $action_controller, 'create' ) );
444
+ add_action( 'admin_post_cnb_update_action', array( $action_controller, 'update' ) );
445
+ add_action( 'admin_post_cnb_actions_bulk', array( $action_controller, 'handle_bulk_actions' ) );
446
+
447
+ $domain_controller = new CnbDomainController();
448
+ add_action( 'admin_post_cnb_create_domain', array( $domain_controller, 'create' ) );
449
+ add_action( 'admin_post_cnb_update_domain', array( $domain_controller, 'update' ) );
450
+ add_action( 'admin_post_cnb_domains_bulk', array( $domain_controller, 'handle_bulk_actions' ) );
451
+
452
+ $profile_controller = new CnbProfileController();
453
+ add_action( 'admin_post_cnb_profile_edit', array( $profile_controller, 'update' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
  }
455
 
456
+ public function register_ajax_actions() {
457
+ $ajax_controller = new CnbAdminAjax();
458
+ add_action( 'wp_ajax_cnb_time_format', array( $ajax_controller, 'time_format' ) );
459
+ add_action( 'wp_ajax_cnb_settings_profile_save', array( $ajax_controller, 'settings_profile_save' ) );
460
+ add_action( 'wp_ajax_cnb_get_checkout', array( $ajax_controller, 'domain_upgrade_get_checkout' ) );
461
+ add_action( 'wp_ajax_cnb_email_activation', array( $ajax_controller, 'cnb_email_activation' ) );
462
+ add_action( 'wp_ajax_cnb_get_plans', array( $ajax_controller, 'get_plans' ) );
463
+
464
+ $action_controller = new CnbActionController();
465
+ add_action( 'wp_ajax_cnb_delete_action', array( $action_controller, 'delete_ajax' ) );
466
+
467
+ $condition_controller = new CnbConditionController();
468
+ add_action( 'wp_ajax_cnb_delete_condition', array( $condition_controller, 'delete_ajax' ) );
469
+
470
+ $domain_controller = new CnbDomainController();
471
+ add_action( 'wp_ajax_cnb_domain_timezone_change', array( $domain_controller, 'update_timezone' ) );
472
  }
473
  }
src/admin/CnbAdminAjax.php CHANGED
@@ -7,6 +7,7 @@ defined( 'ABSPATH' ) || die( '-1' );
7
 
8
  use cnb\admin\api\CnbAppRemote;
9
  use cnb\admin\api\CnbAppRemotePayment;
 
10
  use cnb\admin\models\CnbUser;
11
  use cnb\admin\profile\CnbProfileController;
12
  use WP_Error;
@@ -17,7 +18,8 @@ class CnbAdminAjax {
17
  *
18
  * @return void
19
  */
20
- public static function domain_upgrade_get_checkout() {
 
21
  $planId = filter_input( INPUT_POST, 'planId', FILTER_SANITIZE_STRING );
22
  $domainId = filter_input( INPUT_POST, 'domainId', FILTER_SANITIZE_STRING );
23
 
@@ -62,6 +64,7 @@ class CnbAdminAjax {
62
  'message' => $checkoutSession->checkoutSessionId
63
  ) );
64
  }
 
65
  wp_die();
66
  }
67
 
@@ -69,7 +72,8 @@ class CnbAdminAjax {
69
  * called via jQuery.post
70
  * @return void
71
  */
72
- public static function settings_profile_save() {
 
73
  $data = array();
74
  // Security note: the nonce will be checked via update_user (below),
75
  // and we sanitize the data via filter_var below
@@ -83,9 +87,12 @@ class CnbAdminAjax {
83
 
84
  $result = $controller->update_user( $nonce, $user );
85
  wp_send_json( $result );
 
 
86
  }
87
 
88
- public static function cnb_email_activation() {
 
89
  $admin_url = esc_url( admin_url( 'admin.php' ) );
90
 
91
  $custom_email = trim( filter_input( INPUT_POST, 'admin_email', FILTER_SANITIZE_STRING ) );
@@ -98,6 +105,8 @@ class CnbAdminAjax {
98
  }
99
  }
100
  wp_send_json( $data );
 
 
101
  }
102
 
103
  private static function cnb_time_format_( $time ) {
@@ -107,7 +116,8 @@ class CnbAdminAjax {
107
  return date_i18n( $time_format, $time_formatted );
108
  }
109
 
110
- public static function time_format() {
 
111
  $start = trim( filter_input( INPUT_POST, 'start', FILTER_SANITIZE_STRING ) );
112
  $stop = trim( filter_input( INPUT_POST, 'stop', FILTER_SANITIZE_STRING ) );
113
  wp_send_json( array(
@@ -115,9 +125,14 @@ class CnbAdminAjax {
115
  'stop' => self::cnb_time_format_( $stop ),
116
  )
117
  );
 
 
118
  }
119
 
120
- public static function get_plans() {
 
 
 
121
  $plans = CnbAppRemotePayment::cnb_remote_get_plans();
122
  $eur_yearly_plan = array_filter( $plans, function ( $plan ) {
123
  return $plan->nickname === 'powered-by-eur-yearly';
@@ -131,9 +146,20 @@ class CnbAdminAjax {
131
  $usd_yearly_plan = array_pop( $usd_yearly_plan );
132
  $usd_yearly_per_month = round( $usd_yearly_plan->price / 12.0, 2 );
133
 
 
 
 
 
 
 
 
134
  wp_send_json( array(
135
  'eur_per_month' => $eur_yearly_per_month,
 
136
  'usd_per_month' => $usd_yearly_per_month,
 
137
  ) );
 
 
138
  }
139
  }
7
 
8
  use cnb\admin\api\CnbAppRemote;
9
  use cnb\admin\api\CnbAppRemotePayment;
10
+ use cnb\admin\domain\CnbDomainController;
11
  use cnb\admin\models\CnbUser;
12
  use cnb\admin\profile\CnbProfileController;
13
  use WP_Error;
18
  *
19
  * @return void
20
  */
21
+ public function domain_upgrade_get_checkout() {
22
+ do_action( 'cnb_init', __METHOD__ );
23
  $planId = filter_input( INPUT_POST, 'planId', FILTER_SANITIZE_STRING );
24
  $domainId = filter_input( INPUT_POST, 'domainId', FILTER_SANITIZE_STRING );
25
 
64
  'message' => $checkoutSession->checkoutSessionId
65
  ) );
66
  }
67
+ do_action( 'cnb_finish' );
68
  wp_die();
69
  }
70
 
72
  * called via jQuery.post
73
  * @return void
74
  */
75
+ public function settings_profile_save() {
76
+ do_action( 'cnb_init', __METHOD__ );
77
  $data = array();
78
  // Security note: the nonce will be checked via update_user (below),
79
  // and we sanitize the data via filter_var below
87
 
88
  $result = $controller->update_user( $nonce, $user );
89
  wp_send_json( $result );
90
+ do_action( 'cnb_finish' );
91
+ wp_die();
92
  }
93
 
94
+ public function cnb_email_activation() {
95
+ do_action( 'cnb_init', __METHOD__ );
96
  $admin_url = esc_url( admin_url( 'admin.php' ) );
97
 
98
  $custom_email = trim( filter_input( INPUT_POST, 'admin_email', FILTER_SANITIZE_STRING ) );
105
  }
106
  }
107
  wp_send_json( $data );
108
+ do_action( 'cnb_finish' );
109
+ wp_die();
110
  }
111
 
112
  private static function cnb_time_format_( $time ) {
116
  return date_i18n( $time_format, $time_formatted );
117
  }
118
 
119
+ public function time_format() {
120
+ do_action( 'cnb_init', __METHOD__ );
121
  $start = trim( filter_input( INPUT_POST, 'start', FILTER_SANITIZE_STRING ) );
122
  $stop = trim( filter_input( INPUT_POST, 'stop', FILTER_SANITIZE_STRING ) );
123
  wp_send_json( array(
125
  'stop' => self::cnb_time_format_( $stop ),
126
  )
127
  );
128
+ do_action( 'cnb_finish' );
129
+ wp_die();
130
  }
131
 
132
+ public function get_plans() {
133
+ do_action( 'cnb_init', __METHOD__ );
134
+ $domain_controller = new CnbDomainController();
135
+
136
  $plans = CnbAppRemotePayment::cnb_remote_get_plans();
137
  $eur_yearly_plan = array_filter( $plans, function ( $plan ) {
138
  return $plan->nickname === 'powered-by-eur-yearly';
146
  $usd_yearly_plan = array_pop( $usd_yearly_plan );
147
  $usd_yearly_per_month = round( $usd_yearly_plan->price / 12.0, 2 );
148
 
149
+ $eur_monthly_plan = array_filter( $plans, function ( $plan ) {
150
+ return $plan->nickname === 'powered-by-eur-monthly';
151
+ } );
152
+ $usd_monthly_plan = array_filter( $plans, function ( $plan ) {
153
+ return $plan->nickname === 'powered-by-usd-monthly';
154
+ } );
155
+
156
  wp_send_json( array(
157
  'eur_per_month' => $eur_yearly_per_month,
158
+ 'eur_discount' => $domain_controller->get_discount_percentage($eur_yearly_plan, array_pop($eur_monthly_plan)),
159
  'usd_per_month' => $usd_yearly_per_month,
160
+ 'usd_discount' => $domain_controller->get_discount_percentage($usd_yearly_plan, array_pop($usd_monthly_plan)),
161
  ) );
162
+ do_action( 'cnb_finish' );
163
+ wp_die();
164
  }
165
  }
src/admin/action/CnbActionController.php CHANGED
@@ -38,14 +38,14 @@ class CnbActionController {
38
  *
39
  * @return void
40
  */
41
- public static function delete_ajax() {
 
42
  $cnb_utils = new CnbUtils();
43
  // Action ID
44
  $action_id = $cnb_utils->get_post_val( 'id', null );
45
  $button_id = $cnb_utils->get_post_val( 'bid', null );
46
 
47
- $controller = new CnbActionController();
48
- $result = $controller->deleteWithId( $action_id );
49
  // Instead of sending just the actual result (which is currently ignored anyway)
50
  // We sent both the result and an updated button so the preview code can re-render the button
51
  $return = array(
@@ -53,6 +53,8 @@ class CnbActionController {
53
  'button' => CnbAppRemote::cnb_remote_get_button_full( $button_id )->toArray( false )
54
  );
55
  wp_send_json( $return );
 
 
56
  }
57
 
58
  /**
@@ -104,7 +106,8 @@ class CnbActionController {
104
  * This is called to create an Action
105
  * via `call-now-button.php#cnb_create_action`
106
  */
107
- public static function create() {
 
108
  $cnb_cloud_notifications = array();
109
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
110
  $action = 'cnb-action-edit';
@@ -168,16 +171,11 @@ class CnbActionController {
168
  $url );
169
  }
170
  $redirect_url = esc_url_raw( $redirect_link );
 
171
  wp_safe_redirect( $redirect_url );
172
  exit;
173
  } else {
174
- $url = admin_url( 'admin.php' );
175
- $redirect_link =
176
- add_query_arg(
177
- array(
178
- 'page' => CNB_SLUG
179
- ),
180
- $url );
181
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
182
  'response' => 403,
183
  'back_link' => true,
@@ -185,7 +183,8 @@ class CnbActionController {
185
  }
186
  }
187
 
188
- public static function update() {
 
189
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
190
  $action = 'cnb-action-edit';
191
  $nonce_verified = wp_verify_nonce( $nonce, $action );
@@ -236,17 +235,11 @@ class CnbActionController {
236
  $url );
237
  }
238
  $redirect_url = esc_url_raw( $redirect_link );
 
239
  wp_safe_redirect( $redirect_url );
240
  exit;
241
  } else {
242
- $url = admin_url( 'admin.php' );
243
- $back_link =
244
- add_query_arg(
245
- array(
246
- 'page' => CNB_SLUG . '-actions',
247
- ),
248
- $url );
249
-
250
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
251
  'response' => 403,
252
  'back_link' => true,
@@ -269,7 +262,8 @@ class CnbActionController {
269
  *
270
  * @return void
271
  */
272
- public static function handle_bulk_actions() {
 
273
  $cnb_utils = new CnbUtils();
274
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
275
  $action = 'bulk-cnb_list_actions';
@@ -298,8 +292,11 @@ class CnbActionController {
298
  ),
299
  $url );
300
  $redirect_url = esc_url_raw( $redirect_link );
 
301
  wp_safe_redirect( $redirect_url );
 
302
  } else {
 
303
  wp_die(
304
  esc_html__( 'Unknown Bulk action specified' ),
305
  esc_html__( 'Cannot process Bulk action' ),
@@ -311,6 +308,7 @@ class CnbActionController {
311
  );
312
  }
313
  } else {
 
314
  wp_die(
315
  esc_html__( 'Invalid nonce specified' ),
316
  esc_html__( 'Error' ),
38
  *
39
  * @return void
40
  */
41
+ public function delete_ajax() {
42
+ do_action( 'cnb_init', __METHOD__ );
43
  $cnb_utils = new CnbUtils();
44
  // Action ID
45
  $action_id = $cnb_utils->get_post_val( 'id', null );
46
  $button_id = $cnb_utils->get_post_val( 'bid', null );
47
 
48
+ $result = $this->deleteWithId( $action_id );
 
49
  // Instead of sending just the actual result (which is currently ignored anyway)
50
  // We sent both the result and an updated button so the preview code can re-render the button
51
  $return = array(
53
  'button' => CnbAppRemote::cnb_remote_get_button_full( $button_id )->toArray( false )
54
  );
55
  wp_send_json( $return );
56
+ do_action( 'cnb_finish' );
57
+ wp_die();
58
  }
59
 
60
  /**
106
  * This is called to create an Action
107
  * via `call-now-button.php#cnb_create_action`
108
  */
109
+ public function create() {
110
+ do_action( 'cnb_init', __METHOD__ );
111
  $cnb_cloud_notifications = array();
112
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
113
  $action = 'cnb-action-edit';
171
  $url );
172
  }
173
  $redirect_url = esc_url_raw( $redirect_link );
174
+ do_action( 'cnb_finish' );
175
  wp_safe_redirect( $redirect_url );
176
  exit;
177
  } else {
178
+ do_action( 'cnb_finish' );
 
 
 
 
 
 
179
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
180
  'response' => 403,
181
  'back_link' => true,
183
  }
184
  }
185
 
186
+ public function update() {
187
+ do_action( 'cnb_init', __METHOD__ );
188
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
189
  $action = 'cnb-action-edit';
190
  $nonce_verified = wp_verify_nonce( $nonce, $action );
235
  $url );
236
  }
237
  $redirect_url = esc_url_raw( $redirect_link );
238
+ do_action( 'cnb_finish' );
239
  wp_safe_redirect( $redirect_url );
240
  exit;
241
  } else {
242
+ do_action( 'cnb_finish' );
 
 
 
 
 
 
 
243
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
244
  'response' => 403,
245
  'back_link' => true,
262
  *
263
  * @return void
264
  */
265
+ public function handle_bulk_actions() {
266
+ do_action( 'cnb_init', __METHOD__ );
267
  $cnb_utils = new CnbUtils();
268
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
269
  $action = 'bulk-cnb_list_actions';
292
  ),
293
  $url );
294
  $redirect_url = esc_url_raw( $redirect_link );
295
+ do_action( 'cnb_finish' );
296
  wp_safe_redirect( $redirect_url );
297
+ exit;
298
  } else {
299
+ do_action( 'cnb_finish' );
300
  wp_die(
301
  esc_html__( 'Unknown Bulk action specified' ),
302
  esc_html__( 'Cannot process Bulk action' ),
308
  );
309
  }
310
  } else {
311
+ do_action( 'cnb_finish' );
312
  wp_die(
313
  esc_html__( 'Invalid nonce specified' ),
314
  esc_html__( 'Error' ),
src/admin/action/CnbActionRouter.php CHANGED
@@ -13,7 +13,8 @@ class CnbActionRouter {
13
  *
14
  * @return void
15
  */
16
- public static function render() {
 
17
  $action = ( new CnbUtils() )->get_query_val( 'action', null );
18
  switch ( $action ) {
19
  case 'new':
@@ -29,5 +30,6 @@ class CnbActionRouter {
29
  ( new CnbActionView() )->render();
30
  break;
31
  }
 
32
  }
33
  }
13
  *
14
  * @return void
15
  */
16
+ public function render() {
17
+ do_action( 'cnb_init', __METHOD__ );
18
  $action = ( new CnbUtils() )->get_query_val( 'action', null );
19
  switch ( $action ) {
20
  case 'new':
30
  ( new CnbActionView() )->render();
31
  break;
32
  }
33
+ do_action( 'cnb_finish' );
34
  }
35
  }
src/admin/api/CnbAppRemote.php CHANGED
@@ -11,6 +11,7 @@ use cnb\admin\button\CnbButton;
11
  use cnb\admin\condition\CnbCondition;
12
  use cnb\admin\domain\CnbDomain;
13
  use cnb\admin\models\CnbUser;
 
14
  use JsonSerializable;
15
  use WP_Error;
16
 
@@ -188,7 +189,8 @@ class CnbAppRemote {
188
  public static function cnb_wp_request( $url, $parsed_args ) {
189
  $http = _wp_http_get_object();
190
 
191
- $timer = new RemoteTrace( $url );
 
192
  $response = $http->request( $url, $parsed_args );
193
  $timer->end();
194
 
@@ -248,7 +250,7 @@ class CnbAppRemote {
248
  $url = self::cnb_get_api_base() . $rest_endpoint;
249
  $response = self::wp_remote_patch( $url, $args );
250
  self::cnb_incr_transient_base();
251
- do_action('cnb_after_button_changed');
252
 
253
  return self::cnb_remote_handle_response( $response );
254
  }
@@ -266,7 +268,7 @@ class CnbAppRemote {
266
  $url = self::cnb_get_api_base() . $rest_endpoint;
267
  $response = self::wp_remote_delete( $url, $args );
268
  self::cnb_incr_transient_base();
269
- do_action('cnb_after_button_changed');
270
 
271
  return self::cnb_remote_handle_response( $response );
272
  }
@@ -282,38 +284,47 @@ class CnbAppRemote {
282
  }
283
 
284
  $url = self::cnb_get_api_base() . $rest_endpoint;
285
- $timer = new RemoteTrace( $url );
286
  $response = wp_remote_post( $url, $args );
287
  self::cnb_incr_transient_base();
288
- do_action('cnb_after_button_changed');
289
  $timer->end();
290
 
291
  return self::cnb_remote_handle_response( $response );
292
  }
293
 
 
294
  public static function cnb_remote_get( $rest_endpoint, $authenticated = true ) {
295
- $cnb_get_cache = new CnbGet();
296
- $args = self::cnb_remote_get_args( $authenticated );
297
- if ( $args instanceof WP_Error ) {
298
- return $args;
299
- }
300
 
301
- $url = self::cnb_get_api_base() . $rest_endpoint;
302
- $timer = new RemoteTrace( $url );
303
- $response = $cnb_get_cache->get( $url, $args );
304
- $timer->setCacheHit( $cnb_get_cache->isLastCallCached() );
305
- $timer->end();
306
 
307
- return self::cnb_remote_handle_response( $response );
308
  }
309
 
310
  /**
 
 
 
 
311
  * @return CnbUser|WP_Error
312
  */
313
  public static function cnb_remote_get_user_info() {
 
314
  $rest_endpoint = '/v1/user';
315
 
316
- return CnbUser::fromObject( self::cnb_remote_get( $rest_endpoint ) );
 
 
 
317
  }
318
 
319
  /**
@@ -327,15 +338,30 @@ class CnbAppRemote {
327
  return CnbUser::fromObject( self::cnb_remote_patch( $rest_endpoint, $user ) );
328
  }
329
 
 
 
 
 
 
 
 
 
 
330
  /**
331
  * This returns the domain matching the WordPress domain
 
 
332
  * @return CnbDomain|WP_Error
333
  */
334
  public static function cnb_remote_get_wp_domain() {
 
335
  $cnbAppRemote = new CnbAppRemote();
336
  $rest_endpoint = '/v1/domain/byName/' . $cnbAppRemote->cnb_clean_site_url();
337
 
338
- return CnbDomain::fromObject( self::cnb_remote_get( $rest_endpoint ) );
 
 
 
339
  }
340
 
341
  /**
@@ -734,10 +760,13 @@ class CnbAppRemote {
734
  return CnbApiKey::fromObject( self::cnb_remote_post( $rest_endpoint, $apikey ) );
735
  }
736
 
 
 
 
737
  public static function cnb_remote_create_billing_portal() {
738
  $rest_endpoint = '/v1/stripe/createBillingPortal';
739
 
740
- return self::cnb_remote_post( $rest_endpoint );
741
  }
742
 
743
  /**
11
  use cnb\admin\condition\CnbCondition;
12
  use cnb\admin\domain\CnbDomain;
13
  use cnb\admin\models\CnbUser;
14
+ use cnb\admin\settings\StripeBillingPortal;
15
  use JsonSerializable;
16
  use WP_Error;
17
 
189
  public static function cnb_wp_request( $url, $parsed_args ) {
190
  $http = _wp_http_get_object();
191
 
192
+ $context = __METHOD__ . '<' . $parsed_args['method'] . '>';
193
+ $timer = new RemoteTrace( $url, $context );
194
  $response = $http->request( $url, $parsed_args );
195
  $timer->end();
196
 
250
  $url = self::cnb_get_api_base() . $rest_endpoint;
251
  $response = self::wp_remote_patch( $url, $args );
252
  self::cnb_incr_transient_base();
253
+ do_action( 'cnb_after_button_changed' );
254
 
255
  return self::cnb_remote_handle_response( $response );
256
  }
268
  $url = self::cnb_get_api_base() . $rest_endpoint;
269
  $response = self::wp_remote_delete( $url, $args );
270
  self::cnb_incr_transient_base();
271
+ do_action( 'cnb_after_button_changed' );
272
 
273
  return self::cnb_remote_handle_response( $response );
274
  }
284
  }
285
 
286
  $url = self::cnb_get_api_base() . $rest_endpoint;
287
+ $timer = new RemoteTrace( $url, __METHOD__ );
288
  $response = wp_remote_post( $url, $args );
289
  self::cnb_incr_transient_base();
290
+ do_action( 'cnb_after_button_changed' );
291
  $timer->end();
292
 
293
  return self::cnb_remote_handle_response( $response );
294
  }
295
 
296
+
297
  public static function cnb_remote_get( $rest_endpoint, $authenticated = true ) {
298
+ $cnb_get_cache = new CnbGet();
299
+ $args = self::cnb_remote_get_args( $authenticated );
300
+ if ( $args instanceof WP_Error ) {
301
+ return $args;
302
+ }
303
 
304
+ $url = self::cnb_get_api_base() . $rest_endpoint;
305
+ $timer = new RemoteTrace( $url, __METHOD__ );
306
+ $response = $cnb_get_cache->get( $url, $args );
307
+ $timer->setCacheHit( $cnb_get_cache->isLastCallCached() );
308
+ $timer->end();
309
 
310
+ return self::cnb_remote_handle_response( $response );
311
  }
312
 
313
  /**
314
+ * Returns the User corresponding to the current API key
315
+ *
316
+ * @global CnbUser|null $cnb_user the User corresponding to the current API key
317
+ *
318
  * @return CnbUser|WP_Error
319
  */
320
  public static function cnb_remote_get_user_info() {
321
+ global $cnb_user;
322
  $rest_endpoint = '/v1/user';
323
 
324
+ $user = CnbUser::fromObject( self::cnb_remote_get( $rest_endpoint ) );
325
+ // Only set the global if the User is succesfully retrieved
326
+ if ($user instanceof CnbUser) $cnb_user = $user;
327
+ return $user;
328
  }
329
 
330
  /**
338
  return CnbUser::fromObject( self::cnb_remote_patch( $rest_endpoint, $user ) );
339
  }
340
 
341
+ public static function enable_email_opt_in() {
342
+ $rest_endpoint = '/v1/user/emailPreference';
343
+ self::cnb_remote_post($rest_endpoint);
344
+ }
345
+
346
+ public static function disable_email_opt_in() {
347
+ $rest_endpoint = '/v1/user/emailPreference';
348
+ self::cnb_remote_delete($rest_endpoint);
349
+ }
350
  /**
351
  * This returns the domain matching the WordPress domain
352
+ * @global CnbDomain|null $cnb_domain the domain matching the WordPress domain
353
+ *
354
  * @return CnbDomain|WP_Error
355
  */
356
  public static function cnb_remote_get_wp_domain() {
357
+ global $cnb_domain;
358
  $cnbAppRemote = new CnbAppRemote();
359
  $rest_endpoint = '/v1/domain/byName/' . $cnbAppRemote->cnb_clean_site_url();
360
 
361
+ $domain = CnbDomain::fromObject( self::cnb_remote_get( $rest_endpoint ) );
362
+ // Only set the global if the CnbDomain is succesfully retrieved
363
+ if ($domain instanceof CnbDomain) $cnb_domain = $domain;
364
+ return $domain;
365
  }
366
 
367
  /**
760
  return CnbApiKey::fromObject( self::cnb_remote_post( $rest_endpoint, $apikey ) );
761
  }
762
 
763
+ /**
764
+ * @return StripeBillingPortal
765
+ */
766
  public static function cnb_remote_create_billing_portal() {
767
  $rest_endpoint = '/v1/stripe/createBillingPortal';
768
 
769
+ return StripeBillingPortal::fromObject(self::cnb_remote_post( $rest_endpoint ));
770
  }
771
 
772
  /**
src/admin/api/RemoteTrace.php CHANGED
@@ -2,6 +2,8 @@
2
 
3
  namespace cnb\admin\api;
4
 
 
 
5
  // don't load directly
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
@@ -10,6 +12,16 @@ class RemoteTrace {
10
  * @var string
11
  */
12
  protected $endpoint;
 
 
 
 
 
 
 
 
 
 
13
  /**
14
  * @var float
15
  */
@@ -21,10 +33,13 @@ class RemoteTrace {
21
 
22
  protected $cacheHit = false;
23
 
24
- public function __construct( $endpoint = null ) {
 
25
  $cnb_remoted_traces = RemoteTracer::getInstance();
26
 
27
  $this->endpoint = $endpoint;
 
 
28
  $cnb_remoted_traces->addTrace( $this );
29
  $this->start();
30
  }
@@ -34,10 +49,12 @@ class RemoteTrace {
34
  */
35
  public function start() {
36
  $this->start = microtime( true );
 
37
  }
38
 
39
  public function end() {
40
  $this->end = microtime( true );
 
41
  }
42
 
43
  /**
2
 
3
  namespace cnb\admin\api;
4
 
5
+ use cnb\utils\Cnb_Sentry;
6
+
7
  // don't load directly
8
  defined( 'ABSPATH' ) || die( '-1' );
9
 
12
  * @var string
13
  */
14
  protected $endpoint;
15
+
16
+ /**
17
+ * @var string|null
18
+ */
19
+ protected $context;
20
+ /**
21
+ * @var \Sentry\Tracing\Span|null
22
+ */
23
+ protected $span;
24
+
25
  /**
26
  * @var float
27
  */
33
 
34
  protected $cacheHit = false;
35
 
36
+
37
+ public function __construct( $endpoint = null, $context = null ) {
38
  $cnb_remoted_traces = RemoteTracer::getInstance();
39
 
40
  $this->endpoint = $endpoint;
41
+ $this->context = $context;
42
+
43
  $cnb_remoted_traces->addTrace( $this );
44
  $this->start();
45
  }
49
  */
50
  public function start() {
51
  $this->start = microtime( true );
52
+ $this->span = Cnb_Sentry::start_span($this->endpoint, $this->context);
53
  }
54
 
55
  public function end() {
56
  $this->end = microtime( true );
57
+ Cnb_Sentry::finish_span($this->span);
58
  }
59
 
60
  /**
src/admin/apikey/CnbApiKeyController.php CHANGED
@@ -15,7 +15,8 @@ class CnbApiKeyController {
15
  /**
16
  * This is called via add_action to create a new API key
17
  */
18
- public static function create() {
 
19
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
20
  if ( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb_apikey_create' ) ) {
21
 
@@ -47,9 +48,11 @@ class CnbApiKeyController {
47
  ),
48
  $url );
49
  $redirect_url = esc_url_raw( $redirect_link );
 
50
  wp_safe_redirect( $redirect_url );
51
  exit;
52
  } else {
 
53
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
54
  'response' => 403,
55
  'back_link' => true,
@@ -57,30 +60,6 @@ class CnbApiKeyController {
57
  }
58
  }
59
 
60
- /**
61
- * This is the quick action where they can delete a single Action
62
- *
63
- * It is always called via/with $_GET parameters
64
- *
65
- * @return void
66
- */
67
- public function delete() {
68
- $cnb_utils = new CnbUtils();
69
- $id = $cnb_utils->get_query_val( 'id', null );
70
- $nonce = $cnb_utils->get_query_val( '_wpnonce', null );
71
- $action = 'cnb_delete_apikey';
72
- $nonce_verified = wp_verify_nonce( $nonce, $action );
73
- if ( $nonce_verified ) {
74
- $cnb_cloud_notifications = array();
75
- $apikey = new CnbApiKey();
76
- $adminNotices = CnbAdminNotices::get_instance();
77
- $apikey->id = $id;
78
- CnbAdminCloud::cnb_delete_apikey( $cnb_cloud_notifications, $apikey );
79
-
80
- $adminNotices->notices( $cnb_cloud_notifications );
81
- }
82
- }
83
-
84
  /**
85
  * This is very similar to the <code>delete()</code> function above.
86
  *
@@ -96,7 +75,8 @@ class CnbApiKeyController {
96
  *
97
  * @return void
98
  */
99
- public static function handle_bulk_actions() {
 
100
  $cnb_utils = new CnbUtils();
101
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
102
  $action = 'bulk-cnb_list_apikeys';
@@ -125,8 +105,11 @@ class CnbApiKeyController {
125
  ),
126
  $url );
127
  $redirect_url = esc_url_raw( $redirect_link );
 
128
  wp_safe_redirect( $redirect_url );
 
129
  } else {
 
130
  wp_die(
131
  esc_html__( 'Unknown Bulk action specified' ),
132
  esc_html__( 'Cannot process Bulk action' ),
@@ -138,6 +121,7 @@ class CnbApiKeyController {
138
  );
139
  }
140
  } else {
 
141
  wp_die(
142
  esc_html__( 'Invalid nonce specified' ),
143
  esc_html__( 'Error' ),
@@ -148,4 +132,28 @@ class CnbApiKeyController {
148
  );
149
  }
150
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  }
15
  /**
16
  * This is called via add_action to create a new API key
17
  */
18
+ public function create() {
19
+ do_action( 'cnb_init', __METHOD__ );
20
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
21
  if ( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $nonce, 'cnb_apikey_create' ) ) {
22
 
48
  ),
49
  $url );
50
  $redirect_url = esc_url_raw( $redirect_link );
51
+ do_action( 'cnb_finish' );
52
  wp_safe_redirect( $redirect_url );
53
  exit;
54
  } else {
55
+ do_action( 'cnb_finish' );
56
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
57
  'response' => 403,
58
  'back_link' => true,
60
  }
61
  }
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  /**
64
  * This is very similar to the <code>delete()</code> function above.
65
  *
75
  *
76
  * @return void
77
  */
78
+ public function handle_bulk_actions() {
79
+ do_action( 'cnb_init', __METHOD__ );
80
  $cnb_utils = new CnbUtils();
81
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
82
  $action = 'bulk-cnb_list_apikeys';
105
  ),
106
  $url );
107
  $redirect_url = esc_url_raw( $redirect_link );
108
+ do_action( 'cnb_finish' );
109
  wp_safe_redirect( $redirect_url );
110
+ exit;
111
  } else {
112
+ do_action( 'cnb_finish' );
113
  wp_die(
114
  esc_html__( 'Unknown Bulk action specified' ),
115
  esc_html__( 'Cannot process Bulk action' ),
121
  );
122
  }
123
  } else {
124
+ do_action( 'cnb_finish' );
125
  wp_die(
126
  esc_html__( 'Invalid nonce specified' ),
127
  esc_html__( 'Error' ),
132
  );
133
  }
134
  }
135
+
136
+ /**
137
+ * This is the quick action where they can delete a single Action
138
+ *
139
+ * It is always called via/with $_GET parameters
140
+ *
141
+ * @return void
142
+ */
143
+ public function delete() {
144
+ $cnb_utils = new CnbUtils();
145
+ $id = $cnb_utils->get_query_val( 'id', null );
146
+ $nonce = $cnb_utils->get_query_val( '_wpnonce', null );
147
+ $action = 'cnb_delete_apikey';
148
+ $nonce_verified = wp_verify_nonce( $nonce, $action );
149
+ if ( $nonce_verified ) {
150
+ $cnb_cloud_notifications = array();
151
+ $apikey = new CnbApiKey();
152
+ $adminNotices = CnbAdminNotices::get_instance();
153
+ $apikey->id = $id;
154
+ CnbAdminCloud::cnb_delete_apikey( $cnb_cloud_notifications, $apikey );
155
+
156
+ $adminNotices->notices( $cnb_cloud_notifications );
157
+ }
158
+ }
159
  }
src/admin/apikey/CnbApiKeyRouter.php CHANGED
@@ -11,7 +11,9 @@ defined( 'ABSPATH' ) || die( '-1' );
11
  * Since APIKey creation is handled via admin-post, the only thing we do here is render the overview.
12
  */
13
  class CnbApiKeyRouter {
14
- public static function render() {
 
15
  ( new CnbApiKeyView() )->render();
 
16
  }
17
  }
11
  * Since APIKey creation is handled via admin-post, the only thing we do here is render the overview.
12
  */
13
  class CnbApiKeyRouter {
14
+ public function render() {
15
+ do_action( 'cnb_init', __METHOD__ );
16
  ( new CnbApiKeyView() )->render();
17
+ do_action( 'cnb_finish' );
18
  }
19
  }
src/admin/button/CnbButton.php CHANGED
@@ -180,7 +180,9 @@ class CnbButton implements JsonSerializable {
180
  $options = CnbUtils::getPropertyOrNull( $object, 'options' );
181
  $button->options = CnbButtonOptions::fromObject( $options );
182
  $multiButtonOptions = CnbUtils::getPropertyOrNull( $object, 'multiButtonOptions' );
183
- $button->multiButtonOptions = CnbMultiButtonOptions::fromObject( $multiButtonOptions );
 
 
184
 
185
  if ( gettype( $button->domain ) === 'string' ) {
186
  $domainId = $button->domain;
@@ -257,6 +259,10 @@ class CnbButtonOptions implements JsonSerializable {
257
  public $iconBackgroundColor;
258
  public $iconColor;
259
  public $displayMode;
 
 
 
 
260
 
261
  public static function getAnimationTypes() {
262
  return array(
@@ -275,7 +281,8 @@ class CnbButtonOptions implements JsonSerializable {
275
  'animation' => $this->animation,
276
  'iconBackgroundColor' => $this->iconBackgroundColor,
277
  'iconColor' => $this->iconColor,
278
- 'displayMode' => $this->displayMode
 
279
  );
280
  }
281
 
@@ -287,6 +294,10 @@ class CnbButtonOptions implements JsonSerializable {
287
  $options->iconBackgroundColor = CnbUtils::getPropertyOrNull( $object, 'iconBackgroundColor' );
288
  $options->iconColor = CnbUtils::getPropertyOrNull( $object, 'iconColor' );
289
  $options->displayMode = CnbUtils::getPropertyOrNull( $object, 'displayMode' );
 
 
 
 
290
 
291
  return $options;
292
  }
@@ -340,3 +351,45 @@ class CnbMultiButtonOptions implements JsonSerializable {
340
  return $this->toArray();
341
  }
342
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  $options = CnbUtils::getPropertyOrNull( $object, 'options' );
181
  $button->options = CnbButtonOptions::fromObject( $options );
182
  $multiButtonOptions = CnbUtils::getPropertyOrNull( $object, 'multiButtonOptions' );
183
+ if ($multiButtonOptions != null) {
184
+ $button->multiButtonOptions = CnbMultiButtonOptions::fromObject( $multiButtonOptions );
185
+ }
186
 
187
  if ( gettype( $button->domain ) === 'string' ) {
188
  $domainId = $button->domain;
259
  public $iconBackgroundColor;
260
  public $iconColor;
261
  public $displayMode;
262
+ /**
263
+ * @var CnbScrollOptions
264
+ */
265
+ public $scroll;
266
 
267
  public static function getAnimationTypes() {
268
  return array(
281
  'animation' => $this->animation,
282
  'iconBackgroundColor' => $this->iconBackgroundColor,
283
  'iconColor' => $this->iconColor,
284
+ 'displayMode' => $this->displayMode,
285
+ 'scroll' => $this->scroll,
286
  );
287
  }
288
 
294
  $options->iconBackgroundColor = CnbUtils::getPropertyOrNull( $object, 'iconBackgroundColor' );
295
  $options->iconColor = CnbUtils::getPropertyOrNull( $object, 'iconColor' );
296
  $options->displayMode = CnbUtils::getPropertyOrNull( $object, 'displayMode' );
297
+ $scrollOptions = CnbUtils::getPropertyOrNull( $object, 'scroll' );
298
+ if ($scrollOptions != null) {
299
+ $options->scroll = CnbScrollOptions::fromObject( $scrollOptions );
300
+ }
301
 
302
  return $options;
303
  }
351
  return $this->toArray();
352
  }
353
  }
354
+
355
+ class CnbScrollOptions implements JsonSerializable {
356
+ /**
357
+ * 0-Inf, but should be a positive integer
358
+ * @var number
359
+ */
360
+ public $revealAtHeight;
361
+
362
+ /**
363
+ * 0-Inf, but should be a positive integer
364
+ * @var number
365
+ */
366
+ public $hideAtHeight;
367
+
368
+ /**
369
+ * Indicates if this element should be hidden again after it is visible
370
+ * @var boolean
371
+ */
372
+ public $neverHide;
373
+
374
+ public function toArray() {
375
+ return array(
376
+ 'revealAtHeight'=> $this->revealAtHeight,
377
+ 'hideAtHeight' => $this->hideAtHeight,
378
+ 'neverHide' => $this->neverHide,
379
+ );
380
+ }
381
+
382
+ public static function fromObject( $object ) {
383
+ $options = new CnbScrollOptions();
384
+ $options->revealAtHeight = intval(CnbUtils::getPropertyOrNull( $object, 'revealAtHeight' ));
385
+ $options->hideAtHeight = intval(CnbUtils::getPropertyOrNull( $object, 'hideAtHeight' ));
386
+ // phpcs:ignore PHPCompatibility.FunctionUse
387
+ $options->neverHide = boolval(CnbUtils::getPropertyOrNull( $object, 'neverHide' ));
388
+
389
+ return $options;
390
+ }
391
+
392
+ public function jsonSerialize() {
393
+ return $this->toArray();
394
+ }
395
+ }
src/admin/button/CnbButtonController.php CHANGED
@@ -15,68 +15,12 @@ use cnb\utils\CnbUtils;
15
 
16
  class CnbButtonController {
17
 
18
- private static function create_and_update( $closure ) {
19
- $nonce = filter_input( INPUT_POST, '_wpnonce_button', FILTER_SANITIZE_STRING );
20
- if ( isset( $_REQUEST['_wpnonce_button'] ) && wp_verify_nonce( $nonce, 'cnb-button-edit' ) ) {
21
-
22
- // sanitize the input
23
- $button = filter_input(
24
- INPUT_POST,
25
- 'button',
26
- FILTER_SANITIZE_STRING,
27
- FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
28
- $actions = filter_input(
29
- INPUT_POST,
30
- 'actions',
31
- FILTER_SANITIZE_STRING,
32
- FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
33
- $conditions = filter_input(
34
- INPUT_POST,
35
- 'conditions',
36
- FILTER_SANITIZE_STRING,
37
- FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
38
-
39
- if ( $conditions === null ) {
40
- $conditions = array();
41
- }
42
-
43
- /** @var CnbAction[] */
44
- $processed_actions = array();
45
- if ( is_array( $actions ) ) {
46
- foreach ( $actions as $action ) {
47
- $processed_actions[] = CnbAction::fromObject( $action );
48
- }
49
- }
50
-
51
- /** @var CnbCondition[] */
52
- $processed_conditions = array();
53
- if ( is_array( $conditions ) ) {
54
- foreach ( $conditions as $condition ) {
55
- $processed_conditions[] = CnbCondition::fromObject( $condition );
56
- }
57
- }
58
-
59
- $button['actions'] = $processed_actions;
60
- $button['conditions'] = $processed_conditions;
61
- $processed_button = CnbButton::fromObject( $button );
62
-
63
- // processing
64
- $closure( $processed_button, $processed_actions, $processed_conditions );
65
- // end processing
66
- } else {
67
- wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
68
- 'response' => 403,
69
- 'back_link' => true,
70
- ) );
71
- }
72
-
73
- }
74
-
75
  /**
76
  * This is called to update the button
77
  * via `call-now-button.php#cnb_create_<type>_button`
78
  */
79
- public static function create() {
 
80
  /**
81
  * @param $button CnbButton
82
  *
@@ -113,17 +57,20 @@ class CnbButtonController {
113
  ),
114
  $url );
115
  $redirect_url = esc_url_raw( $redirect_link );
 
116
  wp_safe_redirect( $redirect_url );
117
  exit;
118
  };
119
- self::create_and_update( $inner );
 
120
  }
121
 
122
  /**
123
  * This is called to update the button
124
  * via `call-now-button.php#cnb_update_<type>_button`
125
  */
126
- public static function update() {
 
127
  /**
128
  * @param $button CnbButton
129
  * @param $actions CnbAction[]
@@ -154,64 +101,68 @@ class CnbButtonController {
154
  ),
155
  $url );
156
  $redirect_url = esc_url_raw( $redirect_link );
 
157
  wp_safe_redirect( $redirect_url );
158
  exit;
159
  };
160
- self::create_and_update( $inner );
 
161
  }
162
 
163
- /**
164
- * Quick action, so actually render the Notice
165
- * @return void
166
- */
167
- public function enable_disable() {
168
- $cnb_utils = new CnbUtils();
169
- // "enable" or "disable"
170
- $action = $cnb_utils->get_query_val( 'action', null );
171
- $id = $cnb_utils->get_query_val( 'id', null );
172
- $nonce = $cnb_utils->get_query_val( '_wpnonce', null );
173
- $nonce_verified = wp_verify_nonce( $nonce, 'cnb_enable_disable_button' );
174
- if ( $nonce_verified ) {
175
- $active = $action === 'enable';
176
- $action_verb = $active ? 'enable' : 'disable';
177
- $action_name = $action_verb . 'd';
178
 
179
- $button = CnbAppRemote::cnb_remote_get_button_full( $id );
180
- $button->active = $active;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
- $updated_button = CnbAppRemote::cnb_remote_update_button( $button );
 
 
183
 
184
- if ( ! is_wp_error( $updated_button ) ) {
185
- $notice = new CnbNotice( 'success', '<p>Button <strong>' . esc_html( $updated_button->name ) . '</strong> ' . $action_name . '.</p>', true );
186
- } else {
187
- $notice = CnbAdminCloud::cnb_admin_get_error_message( $action_verb, 'button', $updated_button );
 
 
188
  }
189
- CnbAdminNotices::get_instance()->notice( $notice );
190
- }
191
- }
192
 
193
- /**
194
- * Via the quick actions, be able to delete a Condition
195
- *
196
- * This also means we CANNOT redirect, as we're already halfway into rendering the page,
197
- * so we add the notification to the adminNotices handler
198
- *
199
- * @return void
200
- */
201
- public static function delete() {
202
- $cnb_utils = new CnbUtils();
203
- $id = $cnb_utils->get_query_val( 'id', null );
204
- $nonce = $cnb_utils->get_query_val( '_wpnonce', null );
205
- $action = 'cnb_delete_button';
206
- $nonce_verified = wp_verify_nonce( $nonce, $action );
207
 
208
- if ( $nonce_verified ) {
209
- $adminNotices = CnbAdminNotices::get_instance();
210
- $cnb_cloud_notifications = array();
211
- $button = new CnbButton();
212
- $button->id = $id;
213
- CnbAdminCloud::cnb_delete_button( $cnb_cloud_notifications, $button );
214
- $adminNotices->notices( $cnb_cloud_notifications );
 
 
 
 
 
 
215
  }
216
  }
217
 
@@ -232,7 +183,8 @@ class CnbButtonController {
232
  *
233
  * @return void
234
  */
235
- public static function handle_bulk_actions() {
 
236
  $cnb_utils = new CnbUtils();
237
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
238
  $action = 'bulk-cnb_list_buttons';
@@ -283,8 +235,11 @@ class CnbButtonController {
283
  ),
284
  $url );
285
  $redirect_url = esc_url_raw( $redirect_link );
 
286
  wp_safe_redirect( $redirect_url );
 
287
  } else {
 
288
  wp_die(
289
  esc_html__( 'Invalid nonce specified' ),
290
  esc_html__( 'Error' ),
@@ -295,4 +250,59 @@ class CnbButtonController {
295
  );
296
  }
297
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  }
15
 
16
  class CnbButtonController {
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  /**
19
  * This is called to update the button
20
  * via `call-now-button.php#cnb_create_<type>_button`
21
  */
22
+ public function create() {
23
+ do_action( 'cnb_init', __METHOD__ );
24
  /**
25
  * @param $button CnbButton
26
  *
57
  ),
58
  $url );
59
  $redirect_url = esc_url_raw( $redirect_link );
60
+ do_action( 'cnb_finish' );
61
  wp_safe_redirect( $redirect_url );
62
  exit;
63
  };
64
+ $this->create_and_update( $inner );
65
+ do_action( 'cnb_finish' );
66
  }
67
 
68
  /**
69
  * This is called to update the button
70
  * via `call-now-button.php#cnb_update_<type>_button`
71
  */
72
+ public function update() {
73
+ do_action( 'cnb_init', __METHOD__ );
74
  /**
75
  * @param $button CnbButton
76
  * @param $actions CnbAction[]
101
  ),
102
  $url );
103
  $redirect_url = esc_url_raw( $redirect_link );
104
+ do_action( 'cnb_finish' );
105
  wp_safe_redirect( $redirect_url );
106
  exit;
107
  };
108
+ $this->create_and_update( $inner );
109
+ do_action( 'cnb_finish' );
110
  }
111
 
112
+ private function create_and_update( $closure ) {
113
+ $nonce = filter_input( INPUT_POST, '_wpnonce_button', FILTER_SANITIZE_STRING );
114
+ if ( isset( $_REQUEST['_wpnonce_button'] ) && wp_verify_nonce( $nonce, 'cnb-button-edit' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
+ // sanitize the input
117
+ $button = filter_input(
118
+ INPUT_POST,
119
+ 'button',
120
+ FILTER_SANITIZE_STRING,
121
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
122
+ $actions = filter_input(
123
+ INPUT_POST,
124
+ 'actions',
125
+ FILTER_SANITIZE_STRING,
126
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
127
+ $conditions = filter_input(
128
+ INPUT_POST,
129
+ 'conditions',
130
+ FILTER_SANITIZE_STRING,
131
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
132
 
133
+ if ( $conditions === null ) {
134
+ $conditions = array();
135
+ }
136
 
137
+ /** @var CnbAction[] */
138
+ $processed_actions = array();
139
+ if ( is_array( $actions ) ) {
140
+ foreach ( $actions as $action ) {
141
+ $processed_actions[] = CnbAction::fromObject( $action );
142
+ }
143
  }
 
 
 
144
 
145
+ /** @var CnbCondition[] */
146
+ $processed_conditions = array();
147
+ if ( is_array( $conditions ) ) {
148
+ foreach ( $conditions as $condition ) {
149
+ $processed_conditions[] = CnbCondition::fromObject( $condition );
150
+ }
151
+ }
 
 
 
 
 
 
 
152
 
153
+ $button['actions'] = $processed_actions;
154
+ $button['conditions'] = $processed_conditions;
155
+ $processed_button = CnbButton::fromObject( $button );
156
+
157
+ // processing
158
+ $closure( $processed_button, $processed_actions, $processed_conditions );
159
+ // end processing
160
+ } else {
161
+ do_action( 'cnb_finish' );
162
+ wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
163
+ 'response' => 403,
164
+ 'back_link' => true,
165
+ ) );
166
  }
167
  }
168
 
183
  *
184
  * @return void
185
  */
186
+ public function handle_bulk_actions() {
187
+ do_action( 'cnb_init', __METHOD__ );
188
  $cnb_utils = new CnbUtils();
189
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
190
  $action = 'bulk-cnb_list_buttons';
235
  ),
236
  $url );
237
  $redirect_url = esc_url_raw( $redirect_link );
238
+ do_action( 'cnb_finish' );
239
  wp_safe_redirect( $redirect_url );
240
+ exit;
241
  } else {
242
+ do_action( 'cnb_finish' );
243
  wp_die(
244
  esc_html__( 'Invalid nonce specified' ),
245
  esc_html__( 'Error' ),
250
  );
251
  }
252
  }
253
+
254
+ /**
255
+ * Quick action, so actually render the Notice
256
+ * @return void
257
+ */
258
+ public function enable_disable() {
259
+ $cnb_utils = new CnbUtils();
260
+ // "enable" or "disable"
261
+ $action = $cnb_utils->get_query_val( 'action', null );
262
+ $id = $cnb_utils->get_query_val( 'id', null );
263
+ $nonce = $cnb_utils->get_query_val( '_wpnonce', null );
264
+ $nonce_verified = wp_verify_nonce( $nonce, 'cnb_enable_disable_button' );
265
+ if ( $nonce_verified ) {
266
+ $active = $action === 'enable';
267
+ $action_verb = $active ? 'enable' : 'disable';
268
+ $action_name = $action_verb . 'd';
269
+
270
+ $button = CnbAppRemote::cnb_remote_get_button_full( $id );
271
+ $button->active = $active;
272
+
273
+ $updated_button = CnbAppRemote::cnb_remote_update_button( $button );
274
+
275
+ if ( ! is_wp_error( $updated_button ) ) {
276
+ $notice = new CnbNotice( 'success', '<p>Button <strong>' . esc_html( $updated_button->name ) . '</strong> ' . $action_name . '.</p>', true );
277
+ } else {
278
+ $notice = CnbAdminCloud::cnb_admin_get_error_message( $action_verb, 'button', $updated_button );
279
+ }
280
+ CnbAdminNotices::get_instance()->notice( $notice );
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Via the quick actions, be able to delete a Condition
286
+ *
287
+ * This also means we CANNOT redirect, as we're already halfway into rendering the page,
288
+ * so we add the notification to the adminNotices handler
289
+ *
290
+ * @return void
291
+ */
292
+ public static function delete() {
293
+ $cnb_utils = new CnbUtils();
294
+ $id = $cnb_utils->get_query_val( 'id', null );
295
+ $nonce = $cnb_utils->get_query_val( '_wpnonce', null );
296
+ $action = 'cnb_delete_button';
297
+ $nonce_verified = wp_verify_nonce( $nonce, $action );
298
+
299
+ if ( $nonce_verified ) {
300
+ $adminNotices = CnbAdminNotices::get_instance();
301
+ $cnb_cloud_notifications = array();
302
+ $button = new CnbButton();
303
+ $button->id = $id;
304
+ CnbAdminCloud::cnb_delete_button( $cnb_cloud_notifications, $button );
305
+ $adminNotices->notices( $cnb_cloud_notifications );
306
+ }
307
+ }
308
  }
src/admin/button/CnbButtonRouter.php CHANGED
@@ -14,7 +14,8 @@ class CnbButtonRouter {
14
  *
15
  * @return void
16
  */
17
- public static function render() {
 
18
  $action = ( new CnbUtils() )->get_query_val( 'action', null );
19
  switch ( $action ) {
20
  case 'edit':
@@ -34,5 +35,6 @@ class CnbButtonRouter {
34
  ( new CnbButtonView() )->render();
35
  break;
36
  }
 
37
  }
38
  }
14
  *
15
  * @return void
16
  */
17
+ public function render() {
18
+ do_action( 'cnb_init', __METHOD__ );
19
  $action = ( new CnbUtils() )->get_query_val( 'action', null );
20
  switch ( $action ) {
21
  case 'edit':
35
  ( new CnbButtonView() )->render();
36
  break;
37
  }
38
+ do_action( 'cnb_finish' );
39
  }
40
  }
src/admin/button/CnbButtonView.php CHANGED
@@ -143,9 +143,9 @@ class CnbButtonView {
143
  echo '<!-- Sidebar messages -->';
144
  ( new CnbAdminFunctions() )->cnb_promobox(
145
  'purple',
146
- '50% off the annual plan!',
147
  '<p>Remove the <em>powered by</em> branding from your buttons!</p>
148
- <p>Benefit from this temporary offer and enjoy unlimited access to all features and publish your buttons without branding.</p>',
149
  'flag',
150
  '<strong>&euro;<span class="eur-per-month"></span>/$<span class="usd-per-month"></span> per month</strong>',
151
  'Upgrade',
143
  echo '<!-- Sidebar messages -->';
144
  ( new CnbAdminFunctions() )->cnb_promobox(
145
  'purple',
146
+ '<span class="usd-discount"></span>% off with the annual plan!',
147
  '<p>Remove the <em>powered by</em> branding from your buttons!</p>
148
+ <p>Enjoy unlimited access to all features and publish your buttons without branding.</p>',
149
  'flag',
150
  '<strong>&euro;<span class="eur-per-month"></span>/$<span class="usd-per-month"></span> per month</strong>',
151
  'Upgrade',
src/admin/button/CnbButtonViewEdit.php CHANGED
@@ -325,8 +325,11 @@ class CnbButtonViewEdit {
325
  </tr>
326
 
327
  <?php } else if ( $button->type === 'MULTI' ) {
328
- $backgroundColor = ( $button->options && $button->options->iconBackgroundColor ) ? $button->options->iconBackgroundColor : ( $button->multiButtonOptions->iconBackgroundColor ?: '#009900' );
329
- $iconColor = ( $button->options && $button->options->iconColor ) ? $button->options->iconColor : ( $button->multiButtonOptions->iconColor ?: '#FFFFFF' );
 
 
 
330
  $iconTextOpen = ( $button->multiButtonOptions && $button->multiButtonOptions->iconTextOpen ) ? $button->multiButtonOptions->iconTextOpen : 'more_vert';
331
  $iconTypeOpen = ( $button->multiButtonOptions && $button->multiButtonOptions->iconTypeOpen ) ? $button->multiButtonOptions->iconTypeOpen : 'FONT';
332
  $iconTextClose = ( $button->multiButtonOptions && $button->multiButtonOptions->iconTextClose ) ? $button->multiButtonOptions->iconTextClose : 'close';
@@ -343,7 +346,7 @@ class CnbButtonViewEdit {
343
  color</label></th>
344
  <td>
345
  <input name="button[multiButtonOptions][id]" type="hidden"
346
- value="<?php echo esc_attr( $button->multiButtonOptions->id ); ?>"/>
347
  <input name="button[multiButtonOptions][iconBackgroundColor]"
348
  id="button-multiButtonOptions-iconBackgroundColor" type="text"
349
  value="<?php echo esc_attr( $backgroundColor ); ?>"
@@ -578,6 +581,34 @@ class CnbButtonViewEdit {
578
  </select>
579
  </td>
580
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  <tr class="cnb_hide_on_modal">
582
  <th class="cnb_padding_0">
583
  <h2>Page rules</h2>
325
  </tr>
326
 
327
  <?php } else if ( $button->type === 'MULTI' ) {
328
+ $backgroundColor = ( $button->options && $button->options->iconBackgroundColor ) ? $button->options->iconBackgroundColor :
329
+ ( $button->multiButtonOptions ? $button->multiButtonOptions->iconBackgroundColor : '#009900' );
330
+ $iconColor = ( $button->options && $button->options->iconColor ) ? $button->options->iconColor :
331
+ ( $button->multiButtonOptions ? $button->multiButtonOptions->iconColor : '#FFFFFF' );
332
+ $multi_button_id =( $button->multiButtonOptions && $button->multiButtonOptions->id ) ? $button->multiButtonOptions->id : '';
333
  $iconTextOpen = ( $button->multiButtonOptions && $button->multiButtonOptions->iconTextOpen ) ? $button->multiButtonOptions->iconTextOpen : 'more_vert';
334
  $iconTypeOpen = ( $button->multiButtonOptions && $button->multiButtonOptions->iconTypeOpen ) ? $button->multiButtonOptions->iconTypeOpen : 'FONT';
335
  $iconTextClose = ( $button->multiButtonOptions && $button->multiButtonOptions->iconTextClose ) ? $button->multiButtonOptions->iconTextClose : 'close';
346
  color</label></th>
347
  <td>
348
  <input name="button[multiButtonOptions][id]" type="hidden"
349
+ value="<?php echo esc_attr( $multi_button_id ); ?>"/>
350
  <input name="button[multiButtonOptions][iconBackgroundColor]"
351
  id="button-multiButtonOptions-iconBackgroundColor" type="text"
352
  value="<?php echo esc_attr( $backgroundColor ); ?>"
581
  </select>
582
  </td>
583
  </tr>
584
+ <tr class="cnb_hide_on_modal cnb_advanced_view">
585
+ <?php $reveal_at_height = $button->options->scroll ? $button->options->scroll->revealAtHeight : 0 ?>
586
+ <th><label for="cnb-button-options-scroll-revealatheight">Reveal after scrolling</label></th>
587
+ <td>
588
+ <input name="button[options][scroll][revealAtHeight]" id="cnb-button-options-scroll-revealatheight" type="number" min="0" style="width: 80px" value="<?php echo esc_attr($reveal_at_height) ?>"> pixels from the top
589
+ </td>
590
+ </tr>
591
+ <tr class="cnb_hide_on_modal cnb_advanced_view">
592
+ <?php $hide_at_height = $button->options->scroll ? $button->options->scroll->hideAtHeight : 0 ?>
593
+ <th><label for="cnb-button-options-scroll-hideAtHeight">Hide after scrolling</label></th>
594
+ <td>
595
+ <input name="button[options][scroll][hideAtHeight]" id="cnb-button-options-scroll-hideAtHeight" type="number" min="0" style="width: 80px" value="<?php echo esc_attr($hide_at_height) ?>"> pixels from the top
596
+ <p class="description">hideAtHeight</p>
597
+ </td>
598
+ </tr>
599
+ <tr class="cnb_hide_on_modal cnb_advanced_view">
600
+ <?php $never_hide = $button->options->scroll ? $button->options->scroll->neverHide : false ?>
601
+ <th><label for="cnb-button-options-scroll-neverhide">Never hide</label></th>
602
+ <td>
603
+ <input type="hidden" name="button[options][scroll][neverHide]" value="0"/>
604
+ <input id="cnb-button-options-scroll-neverhide" class="cnb_toggle_checkbox" type="checkbox" name="button[options][scroll][neverHide]"
605
+ value="1" <?php checked( true, $never_hide ); ?> />
606
+ <label for="cnb-button-options-scroll-neverhide" class="cnb_toggle_label">Toggle</label>
607
+ <span data-cnb_toggle_state_label="cnb-button-options-scroll-neverhide" class="cnb_toggle_state cnb_toggle_false">(Inactive)</span>
608
+ <span data-cnb_toggle_state_label="cnb-button-options-scroll-neverhide" class="cnb_toggle_state cnb_toggle_true">Active</span>
609
+ <p class="description">Once this Button is revealed, it will not be hidden again.</p>
610
+ </td>
611
+ </tr>
612
  <tr class="cnb_hide_on_modal">
613
  <th class="cnb_padding_0">
614
  <h2>Page rules</h2>
src/admin/condition/CnbCondition.php CHANGED
@@ -13,8 +13,15 @@ use WP_Error;
13
 
14
  class CnbCondition implements JsonSerializable {
15
  public $id;
 
 
 
16
  public $conditionType = 'URL';
17
  public $filterType;
 
 
 
 
18
  public $matchType;
19
  public $matchValue;
20
 
13
 
14
  class CnbCondition implements JsonSerializable {
15
  public $id;
16
+ /**
17
+ * @var string can be URL or GEO
18
+ */
19
  public $conditionType = 'URL';
20
  public $filterType;
21
+ /**
22
+ * @var string can be one of SIMPLE, EXACT, REGEX, SUBSTRING (for URL).
23
+ * or COUNTRY_CODE (for GEO)
24
+ */
25
  public $matchType;
26
  public $matchValue;
27
 
src/admin/condition/CnbConditionController.php CHANGED
@@ -68,19 +68,20 @@ class CnbConditionController {
68
  *
69
  * @return void
70
  */
71
- public static function delete_ajax() {
 
72
  $cnb_utils = new CnbUtils();
73
  $id = $cnb_utils->get_post_val( 'id', null );
74
 
75
- $controller = new CnbConditionController();
76
- $result = $controller->deleteWithId( $id );
77
  // Instead of sending just the actual result (which is currently ignored anyway)
78
  // We sent both the result and an updated button so the preview code can re-render the button
79
  $return = array(
80
  'result' => $result,
81
  );
82
  wp_send_json( $return );
83
-
 
84
  }
85
 
86
  /**
@@ -89,7 +90,7 @@ class CnbConditionController {
89
  *
90
  * @return void
91
  */
92
- private static function create_and_update_post( $cnb_cloud_notifications, $conditions ) {
93
  $cnb_utils = new CnbUtils();
94
 
95
  // redirect the user to the appropriate page
@@ -123,11 +124,12 @@ class CnbConditionController {
123
  $url );
124
  }
125
  $redirect_url = esc_url_raw( $redirect_link );
 
126
  wp_safe_redirect( $redirect_url );
127
  exit;
128
  }
129
 
130
- private static function create_and_update( $closure, $action ) {
131
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
132
  $nonce_verified = wp_verify_nonce( $nonce, $action );
133
  if ( $nonce_verified ) {
@@ -147,6 +149,7 @@ class CnbConditionController {
147
 
148
  $closure( $processed_conditions );
149
  } else {
 
150
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
151
  'response' => 403,
152
  'back_link' => true,
@@ -158,7 +161,8 @@ class CnbConditionController {
158
  * This is called to create the condition (via POST admin-post.php)
159
  * via `call-now-button.php#cnb_admin_create_condition`
160
  */
161
- public static function create() {
 
162
  /**
163
  * @param $conditions CnbCondition[]
164
  *
@@ -185,18 +189,20 @@ class CnbConditionController {
185
  CnbAdminCloud::cnb_update_button( $cnb_cloud_notifications, $button );
186
  }
187
 
188
- self::create_and_update_post( $cnb_cloud_notifications, $result );
189
 
190
  };
191
  $action = 'cnb_create_condition';
192
- self::create_and_update( $inner, $action );
 
193
  }
194
 
195
  /**
196
  * This is called to update the condition
197
  * via `call-now-button.php#cnb_update_condition`
198
  */
199
- public static function update() {
 
200
  /**
201
  * @param $conditions CnbCondition[]
202
  *
@@ -209,10 +215,11 @@ class CnbConditionController {
209
  // do the processing
210
  $result[] = CnbAdminCloud::cnb_update_condition( $cnb_cloud_notifications, $condition );
211
  }
212
- self::create_and_update_post( $cnb_cloud_notifications, $result );
213
  };
214
  $action = 'cnb_update_condition';
215
- self::create_and_update( $inner, $action );
 
216
  }
217
 
218
  /**
@@ -230,7 +237,8 @@ class CnbConditionController {
230
  *
231
  * @return void
232
  */
233
- public static function handle_bulk_actions() {
 
234
  $cnb_utils = new CnbUtils();
235
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
236
  $action = 'bulk-cnb_list_conditions';
@@ -261,8 +269,11 @@ class CnbConditionController {
261
  ),
262
  $url );
263
  $redirect_url = esc_url_raw( $redirect_link );
 
264
  wp_safe_redirect( $redirect_url );
 
265
  } else {
 
266
  wp_die(
267
  esc_html__( 'Unknown Bulk action specified' ),
268
  esc_html__( 'Cannot process Bulk action' ),
@@ -274,6 +285,7 @@ class CnbConditionController {
274
  );
275
  }
276
  } else {
 
277
  wp_die(
278
  esc_html__( 'Invalid nonce specified' ),
279
  esc_html__( 'Error' ),
68
  *
69
  * @return void
70
  */
71
+ public function delete_ajax() {
72
+ do_action( 'cnb_init', __METHOD__ );
73
  $cnb_utils = new CnbUtils();
74
  $id = $cnb_utils->get_post_val( 'id', null );
75
 
76
+ $result = $this->deleteWithId( $id );
 
77
  // Instead of sending just the actual result (which is currently ignored anyway)
78
  // We sent both the result and an updated button so the preview code can re-render the button
79
  $return = array(
80
  'result' => $result,
81
  );
82
  wp_send_json( $return );
83
+ do_action( 'cnb_finish' );
84
+ wp_die();
85
  }
86
 
87
  /**
90
  *
91
  * @return void
92
  */
93
+ private function create_and_update_post( $cnb_cloud_notifications, $conditions ) {
94
  $cnb_utils = new CnbUtils();
95
 
96
  // redirect the user to the appropriate page
124
  $url );
125
  }
126
  $redirect_url = esc_url_raw( $redirect_link );
127
+ do_action( 'cnb_finish' );
128
  wp_safe_redirect( $redirect_url );
129
  exit;
130
  }
131
 
132
+ private function create_and_update( $closure, $action ) {
133
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
134
  $nonce_verified = wp_verify_nonce( $nonce, $action );
135
  if ( $nonce_verified ) {
149
 
150
  $closure( $processed_conditions );
151
  } else {
152
+ do_action( 'cnb_finish' );
153
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
154
  'response' => 403,
155
  'back_link' => true,
161
  * This is called to create the condition (via POST admin-post.php)
162
  * via `call-now-button.php#cnb_admin_create_condition`
163
  */
164
+ public function create() {
165
+ do_action( 'cnb_init', __METHOD__ );
166
  /**
167
  * @param $conditions CnbCondition[]
168
  *
189
  CnbAdminCloud::cnb_update_button( $cnb_cloud_notifications, $button );
190
  }
191
 
192
+ $this->create_and_update_post( $cnb_cloud_notifications, $result );
193
 
194
  };
195
  $action = 'cnb_create_condition';
196
+ $this->create_and_update( $inner, $action );
197
+ do_action( 'cnb_finish' );
198
  }
199
 
200
  /**
201
  * This is called to update the condition
202
  * via `call-now-button.php#cnb_update_condition`
203
  */
204
+ public function update() {
205
+ do_action( 'cnb_init', __METHOD__ );
206
  /**
207
  * @param $conditions CnbCondition[]
208
  *
215
  // do the processing
216
  $result[] = CnbAdminCloud::cnb_update_condition( $cnb_cloud_notifications, $condition );
217
  }
218
+ $this->create_and_update_post( $cnb_cloud_notifications, $result );
219
  };
220
  $action = 'cnb_update_condition';
221
+ $this->create_and_update( $inner, $action );
222
+ do_action( 'cnb_finish' );
223
  }
224
 
225
  /**
237
  *
238
  * @return void
239
  */
240
+ public function handle_bulk_actions() {
241
+ do_action( 'cnb_init', __METHOD__ );
242
  $cnb_utils = new CnbUtils();
243
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
244
  $action = 'bulk-cnb_list_conditions';
269
  ),
270
  $url );
271
  $redirect_url = esc_url_raw( $redirect_link );
272
+ do_action( 'cnb_finish' );
273
  wp_safe_redirect( $redirect_url );
274
+ exit;
275
  } else {
276
+ do_action( 'cnb_finish' );
277
  wp_die(
278
  esc_html__( 'Unknown Bulk action specified' ),
279
  esc_html__( 'Cannot process Bulk action' ),
285
  );
286
  }
287
  } else {
288
+ do_action( 'cnb_finish' );
289
  wp_die(
290
  esc_html__( 'Invalid nonce specified' ),
291
  esc_html__( 'Error' ),
src/admin/condition/CnbConditionRouter.php CHANGED
@@ -13,7 +13,8 @@ class CnbConditionRouter {
13
  *
14
  * @return void
15
  */
16
- public static function render() {
 
17
  $action = ( new CnbUtils() )->get_query_val( 'action', null );
18
  switch ( $action ) {
19
  case 'new':
@@ -29,5 +30,6 @@ class CnbConditionRouter {
29
  ( new CnbConditionView() )->render();
30
  break;
31
  }
 
32
  }
33
  }
13
  *
14
  * @return void
15
  */
16
+ public function render() {
17
+ do_action( 'cnb_init', __METHOD__ );
18
  $action = ( new CnbUtils() )->get_query_val( 'action', null );
19
  switch ( $action ) {
20
  case 'new':
30
  ( new CnbConditionView() )->render();
31
  break;
32
  }
33
+ do_action( 'cnb_finish' );
34
  }
35
  }
src/admin/condition/CnbConditionViewEdit.php CHANGED
@@ -46,8 +46,6 @@ class CnbConditionViewEdit {
46
  } ?>"/>
47
  <input type="hidden" name="conditions[<?php echo esc_attr( $condition->id ) ?>][delete]"
48
  id="cnb_condition_<?php echo esc_attr( $condition->id ) ?>_delete" value=""/>
49
- <input type="hidden" name="conditions[<?php echo esc_attr( $condition->id ) ?>][conditionType]"
50
- value="<?php echo esc_attr( $condition->conditionType ) ?>"/>
51
  </th>
52
  </tr>
53
  <tr>
@@ -63,16 +61,39 @@ class CnbConditionViewEdit {
63
  </select>
64
  </td>
65
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  <tr>
67
  <th scope="row"><label for="cnb_condition_match_type">Match type</label></th>
68
  <td>
69
  <select id="cnb_condition_match_type"
70
  name="conditions[<?php echo esc_attr( $condition->id ) ?>][matchType]">
71
- <?php foreach ( ( new CnbAdminFunctions() )->cnb_get_condition_match_types() as $condition_match_type_key => $condition_match_type_value ) { ?>
72
- <option value="<?php echo esc_attr( $condition_match_type_key ) ?>"<?php selected( $condition_match_type_key, $condition->matchType ) ?>>
 
73
  <?php echo esc_html( $condition_match_type_value ) ?>
74
  </option>
75
  <?php } ?>
 
 
 
 
 
 
 
 
 
76
  </select>
77
  </td>
78
  </tr>
46
  } ?>"/>
47
  <input type="hidden" name="conditions[<?php echo esc_attr( $condition->id ) ?>][delete]"
48
  id="cnb_condition_<?php echo esc_attr( $condition->id ) ?>_delete" value=""/>
 
 
49
  </th>
50
  </tr>
51
  <tr>
61
  </select>
62
  </td>
63
  </tr>
64
+ <tr class="cnb_advanced_view">
65
+ <th scope="row"><label for="cnb_condition_condition_type">Condition type</label></th>
66
+ <td>
67
+ <select id="cnb_condition_condition_type"
68
+ name="conditions[<?php echo esc_attr( $condition->id ) ?>][conditionType]">
69
+ <?php foreach ( ( new CnbAdminFunctions() )->cnb_get_condition_types() as $type_key => $type_key_value ) { ?>
70
+ <option value="<?php echo esc_attr( $type_key ) ?>"<?php selected( $type_key, $condition->conditionType ) ?>>
71
+ <?php echo esc_html( $type_key_value ) ?>
72
+ </option>
73
+ <?php } ?>
74
+ </select>
75
+ </td>
76
+ </tr>
77
  <tr>
78
  <th scope="row"><label for="cnb_condition_match_type">Match type</label></th>
79
  <td>
80
  <select id="cnb_condition_match_type"
81
  name="conditions[<?php echo esc_attr( $condition->id ) ?>][matchType]">
82
+ <?php
83
+ foreach ( ( new CnbAdminFunctions() )->cnb_get_condition_match_types() as $condition_match_type_key => $condition_match_type_value ) { ?>
84
+ <option class="conditionType conditionType_URL" value="<?php echo esc_attr( $condition_match_type_key ) ?>"<?php selected( $condition_match_type_key, $condition->matchType ) ?>>
85
  <?php echo esc_html( $condition_match_type_value ) ?>
86
  </option>
87
  <?php } ?>
88
+
89
+ <?php
90
+ foreach ( ( new CnbAdminFunctions() )->cnb_get_condition_match_types_geo() as $condition_match_type_key => $condition_match_type_value ) { ?>
91
+ <option class="conditionType conditionType_GEO" value="<?php echo esc_attr( $condition_match_type_key ) ?>"<?php selected( $condition_match_type_key, $condition->matchType ) ?>>
92
+ <?php echo esc_html( $condition_match_type_value ) ?> (Geofencing only)
93
+ </option>
94
+ <?php } ?>
95
+
96
+ ?>
97
  </select>
98
  </td>
99
  </tr>
src/admin/deactivation/Deactivation.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\admin\deactivation;
4
+
5
+ /**
6
+ * On Deactivation of our plugin.
7
+ */
8
+ class Deactivation {
9
+
10
+ /**
11
+ * This is called /during/ the deactivation process (so, not before - there is no change for output).
12
+ *
13
+ * @return void
14
+ */
15
+ static public function onDeactivation() {
16
+ // Noop
17
+ }
18
+
19
+ /**
20
+ * @return void
21
+ */
22
+ public function register_deactivation_popup() {
23
+ global $parent_file;
24
+ if ('plugins.php' === $parent_file) {
25
+ wp_enqueue_script(CNB_SLUG . '-deactivation');
26
+ }
27
+ }
28
+ }
src/admin/domain/CnbDomainController.php CHANGED
@@ -7,6 +7,7 @@ defined( 'ABSPATH' ) || die( '-1' );
7
 
8
  use cnb\admin\api\CnbAdminCloud;
9
  use cnb\admin\api\CnbAppRemote;
 
10
  use cnb\notices\CnbAdminNotices;
11
  use cnb\notices\CnbNotice;
12
  use cnb\utils\CnbUtils;
@@ -16,8 +17,8 @@ class CnbDomainController {
16
  /**
17
  * This is called to create the Domain
18
  */
19
- public static function create() {
20
- $domain_controller = new CnbDomainController();
21
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
22
  $action = 'cnb_create_domain';
23
  $nonce_verified = wp_verify_nonce( $nonce, $action );
@@ -32,7 +33,7 @@ class CnbDomainController {
32
 
33
  $processed_domain = CnbDomain::fromObject( $domain );
34
  // Alligator alert - this is different from other update functions!!
35
- $processed_domain->properties->zindex = $domain_controller->order_to_zindex( $processed_domain->properties->zindex );
36
  // do the processing
37
  $result = CnbAdminCloud::cnb_create_domain( $cnb_cloud_notifications, $processed_domain );
38
 
@@ -53,9 +54,11 @@ class CnbDomainController {
53
  ),
54
  $url );
55
  $redirect_url = esc_url_raw( $redirect_link );
 
56
  wp_safe_redirect( $redirect_url );
57
  exit;
58
  } else {
 
59
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
60
  'response' => 403,
61
  'back_link' => true,
@@ -63,37 +66,13 @@ class CnbDomainController {
63
  }
64
  }
65
 
66
- private static function getDomainFromRequest() {
67
- $domain_controller = new CnbDomainController();
68
- // sanitize the input
69
- $domain = filter_input(
70
- INPUT_POST,
71
- 'domain',
72
- FILTER_SANITIZE_STRING,
73
- FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
74
-
75
- $processed_domain = CnbDomain::fromObject( $domain );
76
- // Alligator alert - this is different from other update functions!!
77
- $processed_domain->properties->zindex = $domain_controller->order_to_zindex( $processed_domain->properties->zindex );
78
-
79
- return $processed_domain;
80
- }
81
-
82
- public function updateWithoutRedirect() {
83
- $domain = self::getDomainFromRequest();
84
- // do the processing
85
- $cnb_cloud_notifications = array();
86
- CnbAdminCloud::cnb_update_domain( $cnb_cloud_notifications, $domain );
87
-
88
- return $cnb_cloud_notifications;
89
- }
90
-
91
- public static function update() {
92
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
93
  $action = 'cnb_update_domain';
94
  $nonce_verified = wp_verify_nonce( $nonce, $action );
95
  if ( $nonce_verified ) {
96
- $domain = self::getDomainFromRequest();
97
  // do the processing
98
  $cnb_cloud_notifications = array();
99
  $result = CnbAdminCloud::cnb_update_domain( $cnb_cloud_notifications, $domain );
@@ -115,9 +94,11 @@ class CnbDomainController {
115
  ),
116
  $url );
117
  $redirect_url = esc_url_raw( $redirect_link );
 
118
  wp_safe_redirect( $redirect_url );
119
  exit;
120
  } else {
 
121
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
122
  'response' => 403,
123
  'back_link' => true,
@@ -125,56 +106,6 @@ class CnbDomainController {
125
  }
126
  }
127
 
128
- public static function updateTimezone() {
129
- if ( isset( $_REQUEST['_wpnonce'] ) && ! empty( $_REQUEST['_wpnonce'] ) ) {
130
- $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
131
- $action = 'cnb_update_domain_timezone';
132
- if ( wp_verify_nonce( $nonce, $action ) ) {
133
- $timezone = filter_input( INPUT_POST, 'timezone', FILTER_SANITIZE_STRING );
134
- $domain = CnbAppRemote::cnb_remote_get_wp_domain();
135
- $domain->timezone = $timezone;
136
- $notifications = array();
137
- CnbAdminCloud::cnb_update_domain( $notifications, $domain );
138
- wp_send_json( array( 'success' => true,
139
- 'domain' => $domain,
140
- 'notification' => $notifications,
141
- 'timezone' => esc_html( $timezone )
142
- ) );
143
-
144
- return;
145
- }
146
- wp_send_json( array( 'success' => false, 'reason' => 'nonce fail' ) );
147
-
148
- return;
149
- }
150
- wp_send_json( array( 'success' => false, 'reason' => 'no nonce' ) );
151
- }
152
-
153
- /**
154
- * Via the quick actions, be able to delete a Domain
155
- *
156
- * This also means we CANNOT redirect, as we're already halfway into rendering the page,
157
- * so we add the notification to the adminNotices handler
158
- *
159
- * @return void
160
- */
161
- public function delete() {
162
- $cnb_utils = new CnbUtils();
163
- $id = $cnb_utils->get_query_val( 'id', null );
164
- $nonce = $cnb_utils->get_query_val( '_wpnonce', null );
165
- $action = 'cnb_delete_domain';
166
- $nonce_verified = wp_verify_nonce( $nonce, $action );
167
-
168
- if ( $nonce_verified ) {
169
- $adminNotices = CnbAdminNotices::get_instance();
170
- $cnb_cloud_notifications = array();
171
- $domain = new CnbDomain();
172
- $domain->id = $id;
173
- CnbAdminCloud::cnb_delete_domain( $cnb_cloud_notifications, $domain );
174
- $adminNotices->notices( $cnb_cloud_notifications );
175
- }
176
- }
177
-
178
  /**
179
  * This is very similar to the <code>delete()</code> function above.
180
  *
@@ -190,7 +121,8 @@ class CnbDomainController {
190
  *
191
  * @return void
192
  */
193
- public static function handle_bulk_actions() {
 
194
  $cnb_utils = new CnbUtils();
195
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
196
  $action = 'bulk-cnb_list_domains';
@@ -221,8 +153,11 @@ class CnbDomainController {
221
  ),
222
  $url );
223
  $redirect_url = esc_url_raw( $redirect_link );
 
224
  wp_safe_redirect( $redirect_url );
 
225
  } else {
 
226
  wp_die(
227
  esc_html__( 'Unknown Bulk action specified' ),
228
  esc_html__( 'Cannot process Bulk action' ),
@@ -245,6 +180,83 @@ class CnbDomainController {
245
  }
246
  }
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  /**
250
  * Convert an "order" integer into a proper zIndex (10 to 2147483647, for example)
@@ -255,7 +267,7 @@ class CnbDomainController {
255
  *
256
  * @return int 2-2147483647
257
  */
258
- function order_to_zindex( $value ) {
259
  $zindexMap = $this->get_zindex_map();
260
  $default = 10;
261
  if (array_key_exists($value, $zindexMap)) {
@@ -274,7 +286,7 @@ class CnbDomainController {
274
  *
275
  * @return int 1-10
276
  */
277
- function zindex_to_order( $zindex ) {
278
  // This starts at the higher number
279
  foreach ( $this->get_zindex_map() as $order => $value ) {
280
  if ( $zindex >= $value ) {
@@ -305,4 +317,14 @@ class CnbDomainController {
305
  1 => 2
306
  );
307
  }
 
 
 
 
 
 
 
 
 
 
308
  }
7
 
8
  use cnb\admin\api\CnbAdminCloud;
9
  use cnb\admin\api\CnbAppRemote;
10
+ use cnb\admin\models\CnbPlan;
11
  use cnb\notices\CnbAdminNotices;
12
  use cnb\notices\CnbNotice;
13
  use cnb\utils\CnbUtils;
17
  /**
18
  * This is called to create the Domain
19
  */
20
+ public function create() {
21
+ do_action( 'cnb_init' );
22
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
23
  $action = 'cnb_create_domain';
24
  $nonce_verified = wp_verify_nonce( $nonce, $action );
33
 
34
  $processed_domain = CnbDomain::fromObject( $domain );
35
  // Alligator alert - this is different from other update functions!!
36
+ $processed_domain->properties->zindex = $this->order_to_zindex( $processed_domain->properties->zindex );
37
  // do the processing
38
  $result = CnbAdminCloud::cnb_create_domain( $cnb_cloud_notifications, $processed_domain );
39
 
54
  ),
55
  $url );
56
  $redirect_url = esc_url_raw( $redirect_link );
57
+ do_action( 'cnb_finish' );
58
  wp_safe_redirect( $redirect_url );
59
  exit;
60
  } else {
61
+ do_action( 'cnb_finish' );
62
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
63
  'response' => 403,
64
  'back_link' => true,
66
  }
67
  }
68
 
69
+ public function update() {
70
+ do_action( 'cnb_init' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
72
  $action = 'cnb_update_domain';
73
  $nonce_verified = wp_verify_nonce( $nonce, $action );
74
  if ( $nonce_verified ) {
75
+ $domain = $this->getDomainFromRequest();
76
  // do the processing
77
  $cnb_cloud_notifications = array();
78
  $result = CnbAdminCloud::cnb_update_domain( $cnb_cloud_notifications, $domain );
94
  ),
95
  $url );
96
  $redirect_url = esc_url_raw( $redirect_link );
97
+ do_action( 'cnb_finish' );
98
  wp_safe_redirect( $redirect_url );
99
  exit;
100
  } else {
101
+ do_action( 'cnb_finish' );
102
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
103
  'response' => 403,
104
  'back_link' => true,
106
  }
107
  }
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  /**
110
  * This is very similar to the <code>delete()</code> function above.
111
  *
121
  *
122
  * @return void
123
  */
124
+ public function handle_bulk_actions() {
125
+ do_action( 'cnb_init', __METHOD__ );
126
  $cnb_utils = new CnbUtils();
127
  $nonce = $cnb_utils->get_post_val( '_wpnonce' );
128
  $action = 'bulk-cnb_list_domains';
153
  ),
154
  $url );
155
  $redirect_url = esc_url_raw( $redirect_link );
156
+ do_action( 'cnb_finish' );
157
  wp_safe_redirect( $redirect_url );
158
+ exit;
159
  } else {
160
+ do_action( 'cnb_finish' );
161
  wp_die(
162
  esc_html__( 'Unknown Bulk action specified' ),
163
  esc_html__( 'Cannot process Bulk action' ),
180
  }
181
  }
182
 
183
+ public function update_timezone() {
184
+ do_action( 'cnb_init', __METHOD__ );
185
+ if ( isset( $_REQUEST['_wpnonce'] ) && ! empty( $_REQUEST['_wpnonce'] ) ) {
186
+ $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
187
+ $action = 'cnb_update_domain_timezone';
188
+ if ( wp_verify_nonce( $nonce, $action ) ) {
189
+ $timezone = filter_input( INPUT_POST, 'timezone', FILTER_SANITIZE_STRING );
190
+ $domain = CnbAppRemote::cnb_remote_get_wp_domain();
191
+ $domain->timezone = $timezone;
192
+ $notifications = array();
193
+ CnbAdminCloud::cnb_update_domain( $notifications, $domain );
194
+ wp_send_json( array( 'success' => true,
195
+ 'domain' => $domain,
196
+ 'notification' => $notifications,
197
+ 'timezone' => esc_html( $timezone )
198
+ ) );
199
+ do_action( 'cnb_finish' );
200
+ return;
201
+ }
202
+ wp_send_json( array( 'success' => false, 'reason' => 'nonce fail' ) );
203
+ do_action( 'cnb_finish' );
204
+ return;
205
+ }
206
+ wp_send_json( array( 'success' => false, 'reason' => 'no nonce' ) );
207
+ do_action( 'cnb_finish' );
208
+ }
209
+
210
+ private function getDomainFromRequest() {
211
+ $domain_controller = new CnbDomainController();
212
+ // sanitize the input
213
+ $domain = filter_input(
214
+ INPUT_POST,
215
+ 'domain',
216
+ FILTER_SANITIZE_STRING,
217
+ FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
218
+
219
+ $processed_domain = CnbDomain::fromObject( $domain );
220
+ // Alligator alert - this is different from other update functions!!
221
+ $processed_domain->properties->zindex = $domain_controller->order_to_zindex( $processed_domain->properties->zindex );
222
+
223
+ return $processed_domain;
224
+ }
225
+
226
+ public function updateWithoutRedirect() {
227
+ $domain = $this->getDomainFromRequest();
228
+ // do the processing
229
+ $cnb_cloud_notifications = array();
230
+ CnbAdminCloud::cnb_update_domain( $cnb_cloud_notifications, $domain );
231
+
232
+ return $cnb_cloud_notifications;
233
+ }
234
+
235
+ /**
236
+ * Via the quick actions, be able to delete a Domain
237
+ *
238
+ * This also means we CANNOT redirect, as we're already halfway into rendering the page,
239
+ * so we add the notification to the adminNotices handler
240
+ *
241
+ * @return void
242
+ */
243
+ public function delete() {
244
+ $cnb_utils = new CnbUtils();
245
+ $id = $cnb_utils->get_query_val( 'id', null );
246
+ $nonce = $cnb_utils->get_query_val( '_wpnonce', null );
247
+ $action = 'cnb_delete_domain';
248
+ $nonce_verified = wp_verify_nonce( $nonce, $action );
249
+
250
+ if ( $nonce_verified ) {
251
+ $adminNotices = CnbAdminNotices::get_instance();
252
+ $cnb_cloud_notifications = array();
253
+ $domain = new CnbDomain();
254
+ $domain->id = $id;
255
+ CnbAdminCloud::cnb_delete_domain( $cnb_cloud_notifications, $domain );
256
+ $adminNotices->notices( $cnb_cloud_notifications );
257
+ }
258
+ }
259
+
260
 
261
  /**
262
  * Convert an "order" integer into a proper zIndex (10 to 2147483647, for example)
267
  *
268
  * @return int 2-2147483647
269
  */
270
+ public function order_to_zindex( $value ) {
271
  $zindexMap = $this->get_zindex_map();
272
  $default = 10;
273
  if (array_key_exists($value, $zindexMap)) {
286
  *
287
  * @return int 1-10
288
  */
289
+ public function zindex_to_order( $zindex ) {
290
  // This starts at the higher number
291
  foreach ( $this->get_zindex_map() as $order => $value ) {
292
  if ( $zindex >= $value ) {
317
  1 => 2
318
  );
319
  }
320
+
321
+ /**
322
+ * @param $plan_year CnbPlan
323
+ * @param $plan_month CnbPlan
324
+ *
325
+ * @return float rounded to ".x", so either "12", or "12.3"
326
+ */
327
+ function get_discount_percentage($plan_year, $plan_month) {
328
+ return round(100 - ($plan_year->price/(12*$plan_month->price)*100), 1);
329
+ }
330
  }
src/admin/domain/CnbDomainRouter.php CHANGED
@@ -13,7 +13,8 @@ class CnbDomainRouter {
13
  *
14
  * @return void
15
  */
16
- public static function render() {
 
17
  $action = ( new CnbUtils() )->get_query_val( 'action', null );
18
  switch ( $action ) {
19
  case 'new':
@@ -32,5 +33,6 @@ class CnbDomainRouter {
32
  ( new CnbDomainView() )->render();
33
  break;
34
  }
 
35
  }
36
  }
13
  *
14
  * @return void
15
  */
16
+ public function render() {
17
+ do_action( 'cnb_init', __METHOD__ );
18
  $action = ( new CnbUtils() )->get_query_val( 'action', null );
19
  switch ( $action ) {
20
  case 'new':
33
  ( new CnbDomainView() )->render();
34
  break;
35
  }
36
+ do_action( 'cnb_finish' );
37
  }
38
  }
src/admin/domain/partials/CnbDomainViewUpgradeFinished.php CHANGED
@@ -59,14 +59,15 @@ class CnbDomainViewUpgradeFinished {
59
  $this->echoBigYaySvg();
60
  echo '</div>';
61
  echo '<h1><strong>' . esc_html( $domain->name ) . '</strong> has been successfully upgraded!</h1>';
62
- echo '<h3>All branding has been removed from your buttons and you\'ll enjoy:</h3>';
63
  echo '<div>';
64
- echo '<div><b>&check;</b> Phone, Email, Location, WhatsApp, Links</div>';
65
- echo '<div><b>&check;</b> Unlimited buttons</div>';
66
- echo '<div><b>&check;</b> Multibutton</div>';
67
- echo '<div><b>&check;</b> Buttonbar (full width with multiple actions)</div>';
68
  echo '<div><b>&check;</b> Advanced page targeting options</div>';
69
  echo '<div><b>&check;</b> Scheduling</div>';
 
70
  echo '</div>';
71
  }
72
 
@@ -76,8 +77,8 @@ class CnbDomainViewUpgradeFinished {
76
  echo $domain->renew == 1 ? ' renew automatically ' : ' expire ';
77
  echo 'on ' . esc_html( date( 'F d, Y', strtotime( $domain->expires ) ) ) . '.';
78
  }
79
- echo 'You can change this on the <a href="' . esc_url( $this->get_settings_url() ) . '">settings page</a>.</p>';
80
- echo '<p>You can access and download your invoice via <a href="' . esc_url( $portal_url->url ) . '">the invoice dashboard</a>. ';
81
  echo 'For any questions, please head over to our <a href="' . esc_url( $support_url ) . '">help center</a>.</p>';
82
  }
83
  }
59
  $this->echoBigYaySvg();
60
  echo '</div>';
61
  echo '<h1><strong>' . esc_html( $domain->name ) . '</strong> has been successfully upgraded!</h1>';
62
+ echo '<h3>All branding has been removed from your buttons and you can enjoy:</h3>';
63
  echo '<div>';
64
+ echo '<div><b>&check;</b> Phone, Email, Location, WhatsApp, Links, Maps, Anchor links with smooth scroll, Signal, Telegram, Facebook Messenger and more.</div>';
65
+ echo '<div><b>&check;</b> Lots of buttons</div>';
66
+ echo '<div><b>&check;</b> Multibutton (expandable multi action single button)</div>';
67
+ echo '<div><b>&check;</b> Buttonbar (full width button with up to 5 actions)</div>';
68
  echo '<div><b>&check;</b> Advanced page targeting options</div>';
69
  echo '<div><b>&check;</b> Scheduling</div>';
70
+ echo '<div><b>&check;</b> And much more!</div>';
71
  echo '</div>';
72
  }
73
 
77
  echo $domain->renew == 1 ? ' renew automatically ' : ' expire ';
78
  echo 'on ' . esc_html( date( 'F d, Y', strtotime( $domain->expires ) ) ) . '.';
79
  }
80
+ echo 'Review your settings on the <a href="' . esc_url( $this->get_settings_url() ) . '">settings page</a>.</p>';
81
+ echo '<p>You can access and download your invoice via <a target="_blank" href="' . esc_url( $portal_url->url ) . '">the invoice dashboard</a>. ';
82
  echo 'For any questions, please head over to our <a href="' . esc_url( $support_url ) . '">help center</a>.</p>';
83
  }
84
  }
src/admin/domain/partials/CnbDomainViewUpgradeOverview.php CHANGED
@@ -109,8 +109,9 @@ class CnbDomainViewUpgradeOverview {
109
  $user = $this->render_hidden_profile();
110
  $this->renderStripeJs();
111
  $this->renderJsForUpgradeForm( $user );
112
- $plans = CnbAppRemotePayment::cnb_remote_get_plans();
113
- $active_currency = $this->getActiveCurrency( $user );
 
114
  ?>
115
  <form id="wp_domain_upgrade" method="post">
116
  <input type="hidden" name="cnb_domain_id" id="cnb_domain_id" value="<?php echo esc_attr( $domain->id ) ?>">
@@ -142,7 +143,7 @@ class CnbDomainViewUpgradeOverview {
142
  $plan_y = round( ( $plan->price / 12.0 ) - floor( $plan->price / 12.0 ), 2 ) * 100;
143
 
144
  $plan_month = $this->get_plan( $plans, 'powered-by-eur-monthly' );
145
- $annual_discount = $plan->price/(12*$plan_month->price)*100;
146
  ?>
147
  <div class="pricebox">
148
  <h3 class="yearly"><span class="cnb-premium-label">PRO </span>Yearly <span class="cnb-green">Save <?php echo esc_html( $annual_discount ); ?>%!</span></h3>
@@ -152,7 +153,7 @@ class CnbDomainViewUpgradeOverview {
152
  class="cents">.<?php echo esc_html( $plan_y ) ?></span><span class="timeframe">/month</span>
153
  </div>
154
  <div class="billingprice">
155
- Billed at €<?php echo esc_html( $plan->price ); ?> annually
156
  </div>
157
  <?php $this->get_profile_edit_modal_link(
158
  'button button-primary',
@@ -197,7 +198,7 @@ class CnbDomainViewUpgradeOverview {
197
  $plan_y = round( ( $plan->price / 12.0 ) - floor( $plan->price / 12.0 ), 2 ) * 100;
198
 
199
  $plan_month = $this->get_plan( $plans, 'powered-by-usd-monthly' );
200
- $annual_discount = $plan->price/(12*$plan_month->price)*100;
201
  ?>
202
  <div class="pricebox">
203
  <h3 class="yearly"><span class="cnb-premium-label">PRO </span>Yearly <span class="cnb-green">Save <?php echo esc_html( $annual_discount ); ?>%!</span></h3>
@@ -207,7 +208,7 @@ class CnbDomainViewUpgradeOverview {
207
  class="cents">.<?php echo esc_html( $plan_y ) ?></span><span class="timeframe">/month</span>
208
  </div>
209
  <div class="billingprice">
210
- Billed at $<?php echo esc_html( $plan->price ) ?> annually
211
  </div>
212
  <?php $this->get_profile_edit_modal_link(
213
  'button button-primary',
109
  $user = $this->render_hidden_profile();
110
  $this->renderStripeJs();
111
  $this->renderJsForUpgradeForm( $user );
112
+ $plans = CnbAppRemotePayment::cnb_remote_get_plans();
113
+ $active_currency = $this->getActiveCurrency( $user );
114
+ $domain_controller = new CnbDomainController();
115
  ?>
116
  <form id="wp_domain_upgrade" method="post">
117
  <input type="hidden" name="cnb_domain_id" id="cnb_domain_id" value="<?php echo esc_attr( $domain->id ) ?>">
143
  $plan_y = round( ( $plan->price / 12.0 ) - floor( $plan->price / 12.0 ), 2 ) * 100;
144
 
145
  $plan_month = $this->get_plan( $plans, 'powered-by-eur-monthly' );
146
+ $annual_discount = $domain_controller->get_discount_percentage($plan, $plan_month)
147
  ?>
148
  <div class="pricebox">
149
  <h3 class="yearly"><span class="cnb-premium-label">PRO </span>Yearly <span class="cnb-green">Save <?php echo esc_html( $annual_discount ); ?>%!</span></h3>
153
  class="cents">.<?php echo esc_html( $plan_y ) ?></span><span class="timeframe">/month</span>
154
  </div>
155
  <div class="billingprice">
156
+ Billed at €<?php echo esc_html( number_format($plan->price, 2) ) ?> annually
157
  </div>
158
  <?php $this->get_profile_edit_modal_link(
159
  'button button-primary',
198
  $plan_y = round( ( $plan->price / 12.0 ) - floor( $plan->price / 12.0 ), 2 ) * 100;
199
 
200
  $plan_month = $this->get_plan( $plans, 'powered-by-usd-monthly' );
201
+ $annual_discount = $domain_controller->get_discount_percentage($plan, $plan_month)
202
  ?>
203
  <div class="pricebox">
204
  <h3 class="yearly"><span class="cnb-premium-label">PRO </span>Yearly <span class="cnb-green">Save <?php echo esc_html( $annual_discount ); ?>%!</span></h3>
208
  class="cents">.<?php echo esc_html( $plan_y ) ?></span><span class="timeframe">/month</span>
209
  </div>
210
  <div class="billingprice">
211
+ Billed at $<?php echo esc_html( number_format($plan->price, 2) ) ?> annually
212
  </div>
213
  <?php $this->get_profile_edit_modal_link(
214
  'button button-primary',
src/admin/legacy/CnbLegacyEdit.php CHANGED
@@ -9,27 +9,230 @@ use cnb\utils\CnbUtils;
9
  defined( 'ABSPATH' ) || die( '-1' );
10
 
11
  class CnbLegacyEdit {
12
- public static function render() {
13
- $cnb_options = get_option( 'cnb' );
14
- $view = new CnbLegacyEdit();
15
- $adminFunctions = new CnbAdminFunctions();
16
- $cnb_utils = new CnbUtils();
17
 
18
  wp_enqueue_script( CNB_SLUG . '-legacy-edit' );
19
 
20
- add_action( 'cnb_header_name', array( $view, 'header' ) );
21
 
22
  do_action( 'cnb_header' );
23
- $view->render_welcome_banner(); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  <div class="cnb-two-column-section">
25
  <div class="cnb-body-column">
26
  <div class="cnb-body-content">
27
 
28
  <h2 class="nav-tab-wrapper">
29
- <a href="<?php echo esc_url( $view->create_tab_url( 'basic_options' ) ) ?>"
30
  class="nav-tab <?php echo esc_attr( $adminFunctions->is_active_tab( 'basic_options' ) ) ?>"
31
  data-tab-name="basic_options">Basics</a>
32
- <a href="<?php echo esc_url( $view->create_tab_url( 'extra_options' ) ) ?>"
33
  class="nav-tab <?php echo esc_attr( $adminFunctions->is_active_tab( 'extra_options' ) ) ?>"
34
  data-tab-name="extra_options">Presentation</a>
35
  </h2>
@@ -207,10 +410,10 @@ class CnbLegacyEdit {
207
  <th colspan="2"><h2>Advanced Settings</h2></th>
208
  </tr>
209
  <?php
210
- $view->render_tracking();
211
- $view->render_conversions();
212
- $view->render_zoom();
213
- $view->render_zindex();
214
  ?>
215
  </table>
216
  <?php submit_button(); ?>
@@ -290,206 +493,7 @@ class CnbLegacyEdit {
290
  </div>
291
  </div>
292
 
293
- <?php
294
- do_action( 'cnb_footer' );
295
- }
296
-
297
- function create_tab_url( $tab ) {
298
- $url = admin_url( 'admin.php' );
299
-
300
- return add_query_arg(
301
- array(
302
- 'page' => 'call-now-button',
303
- 'action' => 'edit',
304
- 'tab' => $tab
305
- ),
306
- $url );
307
- }
308
-
309
- function render_tracking() {
310
- $cnb_options = get_option( 'cnb' );
311
- $cnb_utils = new CnbUtils();
312
- ?>
313
- <tr>
314
- <th scope="row">Click tracking <a
315
- href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/click-tracking/', 'legacy-settings-question-mark', 'click-tracking' ) ) ?>"
316
- target="_blank" class="cnb-nounderscore">
317
- <span class="dashicons dashicons-editor-help"></span>
318
- </a></th>
319
- <td>
320
- <div class="cnb-radio-item">
321
- <input id="tracking3" type="radio" name="cnb[tracking]"
322
- value="0" <?php checked( '0', $cnb_options['tracking'] ); ?> />
323
- <label for="tracking3">Disabled</label>
324
- </div>
325
- <div class="cnb-radio-item">
326
- <input id="tracking4" type="radio" name="cnb[tracking]"
327
- value="3" <?php checked( '3', $cnb_options['tracking'] ); ?> />
328
- <label for="tracking4">Latest Google Analytics (gtag.js)</label>
329
- </div>
330
- <div class="cnb-radio-item">
331
- <input id="tracking1" type="radio" name="cnb[tracking]"
332
- value="2" <?php checked( '2', $cnb_options['tracking'] ); ?> />
333
- <label for="tracking1">Google Universal Analytics (analytics.js)</label>
334
- </div>
335
- <div class="cnb-radio-item">
336
- <input id="tracking2" type="radio" name="cnb[tracking]"
337
- value="1" <?php checked( '1', $cnb_options['tracking'] ); ?> />
338
- <label for="tracking2">Classic Google Analytics (ga.js)</label>
339
- </div>
340
- <p class="description">Using Google Tag Manager? Set up click tracking in GTM. <a
341
- href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/google-tag-manager-event-tracking/', 'legacy-settings-description', 'google-tag-manager-event-tracking' ) ) ?>"
342
- target="_blank">Learn how to do this...</a></p>
343
- </td>
344
- </tr>
345
- <?php
346
- }
347
-
348
- function render_conversions() {
349
- $cnb_options = get_option( 'cnb' );
350
- $cnb_utils = new CnbUtils();
351
- ?>
352
- <tr>
353
- <th scope="row">Google Ads <a
354
- href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/google-ads/', 'legacy-settings-question-mark', 'google-ads' ) ) ?>"
355
- target="_blank" class="cnb-nounderscore">
356
- <span class="dashicons dashicons-editor-help"></span>
357
- </a></th>
358
- <td class="conversions">
359
- <div class="cnb-radio-item">
360
- <input id="cnb_conversions_0" name="cnb[conversions]" type="radio"
361
- value="0" <?php checked( '0', $cnb_options['conversions'] ); ?> /> <label
362
- for="cnb_conversions_0">Off </label>
363
- </div>
364
- <div class="cnb-radio-item">
365
- <input id="cnb_conversions_1" name="cnb[conversions]" type="radio"
366
- value="1" <?php checked( '1', $cnb_options['conversions'] ); ?> /> <label
367
- for="cnb_conversions_1">Conversion Tracking using Google's global site tag </label>
368
- </div>
369
- <div class="cnb-radio-item">
370
- <input id="cnb_conversions_2" name="cnb[conversions]" type="radio"
371
- value="2" <?php checked( '2', $cnb_options['conversions'] ); ?> /> <label
372
- for="cnb_conversions_2">Conversion Tracking using JavaScript</label>
373
- </div>
374
- <p class="description">Select this option if you want to track clicks on the button as Google Ads
375
- conversions. This option requires the Event snippet to be present on the page. <a
376
- href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/google-ads/', 'legacy-settings-description', 'google-ads' ) ) ?>"
377
- target="_blank">Learn more...</a></p>
378
- </td>
379
- </tr>
380
- <?php
381
- }
382
-
383
- function render_zoom() {
384
- $cnb_options = get_option( 'cnb' );
385
- ?>
386
- <tr class="zoom">
387
- <th scope="row"><label for="cnb_slider">Button size <span id="cnb_slider_value"></span></label></th>
388
- <td>
389
- <label class="cnb_slider_value">Smaller&nbsp;&laquo;&nbsp;</label>
390
- <input type="range" min="0.7" max="1.3" name="cnb[zoom]"
391
- value="<?php echo esc_attr( $cnb_options['zoom'] ) ?>" class="slider" id="cnb_slider" step="0.1">
392
- <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Bigger</label>
393
- </td>
394
- </tr>
395
- <?php
396
- }
397
-
398
- function render_zindex() {
399
- $cnb_options = get_option( 'cnb' );
400
- $cnb_utils = new CnbUtils();
401
- ?>
402
- <tr class="z-index">
403
- <th scope="row"><label for="cnb_order_slider">Order (<span id="cnb_order_value"></span>)</label> <a
404
- href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/set-order/', 'legacy-settings-question-mark', 'Order' ) ) ?>"
405
- target="_blank"
406
- class="cnb-nounderscore">
407
- <span class="dashicons dashicons-editor-help"></span>
408
- </a></th>
409
- <td>
410
- <label class="cnb_slider_value">Backwards&nbsp;&laquo;&nbsp;</label>
411
- <input type="range" min="1" max="10" name="cnb[z-index]"
412
- value="<?php echo esc_attr( $cnb_options['z-index'] ) ?>" class="slider2" id="cnb_order_slider"
413
- step="1">
414
- <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Front</label>
415
- <p class="description">The default (and recommended) value is all the way to the front so the button
416
- sits on top of everything else. In case you have a specific usecase where you want something else to
417
- sit in front of the Call Now Button (e.g. a chat window or a cookie notice) you can move this
418
- backwards one step at a time to adapt it to your situation.</p>
419
- </td>
420
- </tr>
421
-
422
  <?php
423
  }
424
 
425
- function header() {
426
- echo esc_html( CNB_NAME ) . ' <span class="cnb-version">v' . esc_html( CNB_VERSION ) . '</span>';
427
- }
428
-
429
- function render_welcome_banner() {
430
- $legacyController = new CnbLegacyController();
431
- $cnb_utils = new CnbUtils();
432
- if ( ! $legacyController->show_welcome_banner() ) {
433
- return;
434
- }
435
- $dismiss_value = 'welcome-panel';
436
-
437
- $url = admin_url( 'admin.php' );
438
- $upgrade_link =
439
- add_query_arg(
440
- array( 'page' => 'call-now-button-upgrade' ),
441
- $url );
442
-
443
- $dismiss_url = add_query_arg( array(
444
- CNB_SLUG . '_dismiss' => $dismiss_value
445
- ), $url );
446
-
447
- ?>
448
- <div id="welcome-banner"
449
- class="welcome-banner is-dismissible notice-call-now-button"
450
- data-dismiss-url="<?php echo esc_url( $dismiss_url ) ?>">
451
- <div class="welcome-banner-content">
452
- <h2>Welcome to Call&nbsp;Now&nbsp;Button version&nbsp;1.1</h2>
453
- <div class="welcome-banner-column-container">
454
- <div class="welcome-banner-column">
455
- <h3>Some cool stats!</h3>
456
- <div class="welcome-column-box">
457
- <p class="cnb-mobile-inline">🎉 The #1 click-to-call button on WordPress for 10 years!</p>
458
- <p class="cnb-mobile-inline">🚀 200k+ active installations and growing every day!</p>
459
- <p class="cnb-mobile-inline">❤️ Loved by our users and rated 4.9!</p>
460
- <p class="cnb-mobile-inline">💎 Call Now Button <strong>Premium</strong> is SO GOOD!!
461
- </p>
462
- </div>
463
- </div>
464
- <div class="welcome-banner-column">
465
- <h3>What's in Premium?</h3>
466
- <p class="cnb-mobile-inline">+ Create multiple buttons</p>
467
- <p class="cnb-mobile-inline">+ WhatsApp, SMS/text, Email, Maps and Links</p>
468
- <p class="cnb-mobile-inline">+ WhatsApp chat modal</p>
469
- <p class="cnb-mobile-inline">+ Multi action buttons</p>
470
- <p class="cnb-mobile-inline">+ Button scheduler</p>
471
- <p class="cnb-mobile-inline">+ Icon selection</p>
472
- <p class="cnb-mobile-inline">+ Advanced page targeting</p>
473
- <p class="cnb-mobile-inline">+ Live preview</p>
474
- </div>
475
- <div class="welcome-banner-column">
476
- <a class="button button-primary button-hero" href="<?php echo esc_url( $upgrade_link ) ?>">Get
477
- Premium Free</a>
478
-
479
- <p><a href="<?php echo esc_url( $upgrade_link ) ?>">More info about Premium</a></p>
480
- <h3>Other resources</h3>
481
- <p>
482
- <a href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/', 'welcome-banner', 'Help center' ) ) ?>">Help
483
- center</a></p>
484
- <p>
485
- <a href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/#faq', 'welcome-banner', 'FAQ' ) ) ?>">FAQ</a>
486
- </p>
487
- </div>
488
- </div>
489
- </div>
490
- <button type="button" class="notice-dismiss"><span
491
- class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.' ) ?></span></button>
492
- </div>
493
- <?php }
494
-
495
  }
9
  defined( 'ABSPATH' ) || die( '-1' );
10
 
11
  class CnbLegacyEdit {
12
+ public function render() {
13
+ do_action( 'cnb_init', __METHOD__ );
 
 
 
14
 
15
  wp_enqueue_script( CNB_SLUG . '-legacy-edit' );
16
 
17
+ add_action( 'cnb_header_name', array( $this, 'header' ) );
18
 
19
  do_action( 'cnb_header' );
20
+ $this->render_welcome_banner();
21
+ $this->render_form();
22
+ do_action( 'cnb_footer' );
23
+ do_action( 'cnb_finish' );
24
+ }
25
+
26
+ private function create_tab_url( $tab ) {
27
+ $url = admin_url( 'admin.php' );
28
+
29
+ return add_query_arg(
30
+ array(
31
+ 'page' => 'call-now-button',
32
+ 'action' => 'edit',
33
+ 'tab' => $tab
34
+ ),
35
+ $url );
36
+ }
37
+
38
+ public function render_tracking() {
39
+ $cnb_options = get_option( 'cnb' );
40
+ $cnb_utils = new CnbUtils();
41
+ ?>
42
+ <tr>
43
+ <th scope="row">Click tracking <a
44
+ href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/click-tracking/', 'legacy-settings-question-mark', 'click-tracking' ) ) ?>"
45
+ target="_blank" class="cnb-nounderscore">
46
+ <span class="dashicons dashicons-editor-help"></span>
47
+ </a></th>
48
+ <td>
49
+ <div class="cnb-radio-item">
50
+ <input id="tracking3" type="radio" name="cnb[tracking]"
51
+ value="0" <?php checked( '0', $cnb_options['tracking'] ); ?> />
52
+ <label for="tracking3">Disabled</label>
53
+ </div>
54
+ <div class="cnb-radio-item">
55
+ <input id="tracking4" type="radio" name="cnb[tracking]"
56
+ value="3" <?php checked( '3', $cnb_options['tracking'] ); ?> />
57
+ <label for="tracking4">Latest Google Analytics (gtag.js)</label>
58
+ </div>
59
+ <div class="cnb-radio-item">
60
+ <input id="tracking1" type="radio" name="cnb[tracking]"
61
+ value="2" <?php checked( '2', $cnb_options['tracking'] ); ?> />
62
+ <label for="tracking1">Google Universal Analytics (analytics.js)</label>
63
+ </div>
64
+ <div class="cnb-radio-item">
65
+ <input id="tracking2" type="radio" name="cnb[tracking]"
66
+ value="1" <?php checked( '1', $cnb_options['tracking'] ); ?> />
67
+ <label for="tracking2">Classic Google Analytics (ga.js)</label>
68
+ </div>
69
+ <p class="description">Using Google Tag Manager? Set up click tracking in GTM. <a
70
+ href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/google-tag-manager-event-tracking/', 'legacy-settings-description', 'google-tag-manager-event-tracking' ) ) ?>"
71
+ target="_blank">Learn how to do this...</a></p>
72
+ </td>
73
+ </tr>
74
+ <?php
75
+ }
76
+
77
+ public function render_conversions() {
78
+ $cnb_options = get_option( 'cnb' );
79
+ $cnb_utils = new CnbUtils();
80
+ ?>
81
+ <tr>
82
+ <th scope="row">Google Ads <a
83
+ href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/google-ads/', 'legacy-settings-question-mark', 'google-ads' ) ) ?>"
84
+ target="_blank" class="cnb-nounderscore">
85
+ <span class="dashicons dashicons-editor-help"></span>
86
+ </a></th>
87
+ <td class="conversions">
88
+ <div class="cnb-radio-item">
89
+ <input id="cnb_conversions_0" name="cnb[conversions]" type="radio"
90
+ value="0" <?php checked( '0', $cnb_options['conversions'] ); ?> /> <label
91
+ for="cnb_conversions_0">Off </label>
92
+ </div>
93
+ <div class="cnb-radio-item">
94
+ <input id="cnb_conversions_1" name="cnb[conversions]" type="radio"
95
+ value="1" <?php checked( '1', $cnb_options['conversions'] ); ?> /> <label
96
+ for="cnb_conversions_1">Conversion Tracking using Google's global site tag </label>
97
+ </div>
98
+ <div class="cnb-radio-item">
99
+ <input id="cnb_conversions_2" name="cnb[conversions]" type="radio"
100
+ value="2" <?php checked( '2', $cnb_options['conversions'] ); ?> /> <label
101
+ for="cnb_conversions_2">Conversion Tracking using JavaScript</label>
102
+ </div>
103
+ <p class="description">Select this option if you want to track clicks on the button as Google Ads
104
+ conversions. This option requires the Event snippet to be present on the page. <a
105
+ href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/google-ads/', 'legacy-settings-description', 'google-ads' ) ) ?>"
106
+ target="_blank">Learn more...</a></p>
107
+ </td>
108
+ </tr>
109
+ <?php
110
+ }
111
+
112
+ public function render_zoom() {
113
+ $cnb_options = get_option( 'cnb' );
114
+ ?>
115
+ <tr class="zoom">
116
+ <th scope="row"><label for="cnb_slider">Button size <span id="cnb_slider_value"></span></label></th>
117
+ <td>
118
+ <label class="cnb_slider_value">Smaller&nbsp;&laquo;&nbsp;</label>
119
+ <input type="range" min="0.7" max="1.3" name="cnb[zoom]"
120
+ value="<?php echo esc_attr( $cnb_options['zoom'] ) ?>" class="slider" id="cnb_slider" step="0.1">
121
+ <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Bigger</label>
122
+ </td>
123
+ </tr>
124
+ <?php
125
+ }
126
+
127
+ public function render_zindex() {
128
+ $cnb_options = get_option( 'cnb' );
129
+ $cnb_utils = new CnbUtils();
130
+ ?>
131
+ <tr class="z-index">
132
+ <th scope="row"><label for="cnb_order_slider">Order (<span id="cnb_order_value"></span>)</label> <a
133
+ href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/settings/set-order/', 'legacy-settings-question-mark', 'Order' ) ) ?>"
134
+ target="_blank"
135
+ class="cnb-nounderscore">
136
+ <span class="dashicons dashicons-editor-help"></span>
137
+ </a></th>
138
+ <td>
139
+ <label class="cnb_slider_value">Backwards&nbsp;&laquo;&nbsp;</label>
140
+ <input type="range" min="1" max="10" name="cnb[z-index]"
141
+ value="<?php echo esc_attr( $cnb_options['z-index'] ) ?>" class="slider2" id="cnb_order_slider"
142
+ step="1">
143
+ <label class="cnb_slider_value">&nbsp;&raquo;&nbsp;Front</label>
144
+ <p class="description">The default (and recommended) value is all the way to the front so the button
145
+ sits on top of everything else. In case you have a specific usecase where you want something else to
146
+ sit in front of the Call Now Button (e.g. a chat window or a cookie notice) you can move this
147
+ backwards one step at a time to adapt it to your situation.</p>
148
+ </td>
149
+ </tr>
150
+
151
+ <?php
152
+ }
153
+
154
+ public function header() {
155
+ echo esc_html( CNB_NAME ) . ' <span class="cnb-version">v' . esc_html( CNB_VERSION ) . '</span>';
156
+ }
157
+
158
+ private function render_welcome_banner() {
159
+ $legacyController = new CnbLegacyController();
160
+ $cnb_utils = new CnbUtils();
161
+ if ( ! $legacyController->show_welcome_banner() ) {
162
+ return;
163
+ }
164
+ $dismiss_value = 'welcome-panel';
165
+
166
+ $url = admin_url( 'admin.php' );
167
+ $upgrade_link =
168
+ add_query_arg(
169
+ array( 'page' => 'call-now-button-upgrade' ),
170
+ $url );
171
+
172
+ $dismiss_url = add_query_arg( array(
173
+ CNB_SLUG . '_dismiss' => $dismiss_value
174
+ ), $url );
175
+
176
+ ?>
177
+ <div id="welcome-banner"
178
+ class="welcome-banner is-dismissible notice-call-now-button"
179
+ data-dismiss-url="<?php echo esc_url( $dismiss_url ) ?>">
180
+ <div class="welcome-banner-content">
181
+ <h2>Welcome to Call&nbsp;Now&nbsp;Button!</h2>
182
+ <div class="welcome-banner-column-container">
183
+ <div class="welcome-banner-column">
184
+ <h3>Some cool stats!</h3>
185
+ <div class="welcome-column-box">
186
+ <p class="cnb-mobile-inline">🎉 The #1 click-to-call button on WordPress for 10 years!</p>
187
+ <p class="cnb-mobile-inline">🚀 200k+ active installations and growing every day!</p>
188
+ <p class="cnb-mobile-inline">❤️ Loved by our users and rated 4.9!</p>
189
+ <p class="cnb-mobile-inline">💎 Call Now Button <strong>Premium</strong> has so much more!
190
+ </p>
191
+ </div>
192
+ </div>
193
+ <div class="welcome-banner-column">
194
+ <h3>What's in Premium?</h3>
195
+ <p class="cnb-mobile-inline">+ Create lots of buttons</p>
196
+ <p class="cnb-mobile-inline">+ Phone, SMS, Email, Maps, Links, Anchors</p>
197
+ <p class="cnb-mobile-inline">+ WhatsApp, Signal, Telegram, FB Messenger</p>
198
+ <p class="cnb-mobile-inline">+ Multi action buttons</p>
199
+ <p class="cnb-mobile-inline">+ Button scheduler</p>
200
+ <p class="cnb-mobile-inline">And much more!</p>
201
+ </div>
202
+ <div class="welcome-banner-column">
203
+ <a class="button button-primary button-hero" href="<?php echo esc_url( $upgrade_link ) ?>">Get
204
+ Premium Free</a>
205
+
206
+ <p><a href="<?php echo esc_url( $upgrade_link ) ?>">More info about Premium</a></p>
207
+ <h3>Other resources</h3>
208
+ <p>
209
+ <a href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/', 'welcome-banner', 'Help center' ) ) ?>">Help
210
+ center</a></p>
211
+ <p>
212
+ <a href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/#faq', 'welcome-banner', 'FAQ' ) ) ?>">FAQ</a>
213
+ </p>
214
+ </div>
215
+ </div>
216
+ </div>
217
+ <button type="button" class="notice-dismiss"><span
218
+ class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.' ) ?></span></button>
219
+ </div>
220
+ <?php }
221
+
222
+ private function render_form() {
223
+ $cnb_options = get_option( 'cnb' );
224
+ $adminFunctions = new CnbAdminFunctions();
225
+ $cnb_utils = new CnbUtils();
226
+ ?>
227
  <div class="cnb-two-column-section">
228
  <div class="cnb-body-column">
229
  <div class="cnb-body-content">
230
 
231
  <h2 class="nav-tab-wrapper">
232
+ <a href="<?php echo esc_url( $this->create_tab_url( 'basic_options' ) ) ?>"
233
  class="nav-tab <?php echo esc_attr( $adminFunctions->is_active_tab( 'basic_options' ) ) ?>"
234
  data-tab-name="basic_options">Basics</a>
235
+ <a href="<?php echo esc_url( $this->create_tab_url( 'extra_options' ) ) ?>"
236
  class="nav-tab <?php echo esc_attr( $adminFunctions->is_active_tab( 'extra_options' ) ) ?>"
237
  data-tab-name="extra_options">Presentation</a>
238
  </h2>
410
  <th colspan="2"><h2>Advanced Settings</h2></th>
411
  </tr>
412
  <?php
413
+ $this->render_tracking();
414
+ $this->render_conversions();
415
+ $this->render_zoom();
416
+ $this->render_zindex();
417
  ?>
418
  </table>
419
  <?php submit_button(); ?>
493
  </div>
494
  </div>
495
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
  <?php
497
  }
498
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  }
src/admin/legacy/CnbLegacyUpgrade.php CHANGED
@@ -122,25 +122,26 @@ class CnbLegacyUpgrade {
122
  </div>
123
  <?php }
124
 
125
- public static function render() {
 
126
  wp_enqueue_script( CNB_SLUG . '-settings' );
127
- $view = new CnbLegacyUpgrade();
128
 
129
- add_action( 'cnb_header_name', array( $view, 'header' ) );
130
  do_action( 'cnb_header' );
131
  ?>
132
 
133
  <div class="cnb-one-column-section">
134
  <div class="cnb-body-content">
135
  <div class="cnb-two-promobox-row">
136
- <?php $view->standard_plugin_promobox() ?>
137
- <?php $view->premium_plugin_promobox() ?>
138
  </div>
139
- <?php $view->upgrade_faq() ?>
140
  </div>
141
  </div>
142
  <hr>
143
  <?php
144
  do_action( 'cnb_footer' );
 
145
  }
146
  }
122
  </div>
123
  <?php }
124
 
125
+ public function render() {
126
+ do_action( 'cnb_init', __METHOD__ );
127
  wp_enqueue_script( CNB_SLUG . '-settings' );
 
128
 
129
+ add_action( 'cnb_header_name', array( $this, 'header' ) );
130
  do_action( 'cnb_header' );
131
  ?>
132
 
133
  <div class="cnb-one-column-section">
134
  <div class="cnb-body-content">
135
  <div class="cnb-two-promobox-row">
136
+ <?php $this->standard_plugin_promobox() ?>
137
+ <?php $this->premium_plugin_promobox() ?>
138
  </div>
139
+ <?php $this->upgrade_faq() ?>
140
  </div>
141
  </div>
142
  <hr>
143
  <?php
144
  do_action( 'cnb_footer' );
145
+ do_action( 'cnb_finish' );
146
  }
147
  }
src/admin/models/CnbUser.php CHANGED
@@ -57,6 +57,11 @@ class CnbUser implements JsonSerializable {
57
  */
58
  public $euvatbusiness;
59
 
 
 
 
 
 
60
  /**
61
  * If a stdClass is passed, it is transformed into a CnbButton.
62
  * a WP_Error is ignored and return immediatly
@@ -85,12 +90,15 @@ class CnbUser implements JsonSerializable {
85
  $user->euvatbusiness = CnbUtils::getPropertyOrNull( $object, 'euvatbusiness' );
86
  $stripeDetails = CnbUserStripeDetails::fromObject( CnbUtils::getPropertyOrNull( $object, 'stripeDetails' ) );
87
  $user->stripeDetails = $stripeDetails;
 
 
88
 
89
  return $user;
90
  }
91
 
92
  public function toArray() {
93
  // Note, we do not export "euvatbusiness", since that is only used internally
 
94
  return array(
95
  'id' => $this->id,
96
  'name' => $this->name,
@@ -258,3 +266,50 @@ class CnbUserStripeDetails implements JsonSerializable {
258
  return $this->toArray();
259
  }
260
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  */
58
  public $euvatbusiness;
59
 
60
+ /**
61
+ * @var CnbUserMarketingData
62
+ */
63
+ public $marketingData;
64
+
65
  /**
66
  * If a stdClass is passed, it is transformed into a CnbButton.
67
  * a WP_Error is ignored and return immediatly
90
  $user->euvatbusiness = CnbUtils::getPropertyOrNull( $object, 'euvatbusiness' );
91
  $stripeDetails = CnbUserStripeDetails::fromObject( CnbUtils::getPropertyOrNull( $object, 'stripeDetails' ) );
92
  $user->stripeDetails = $stripeDetails;
93
+ $marketing_data = CnbUserMarketingData::fromObject( CnbUtils::getPropertyOrNull( $object, 'marketingData' ) );
94
+ $user->marketingData = $marketing_data;
95
 
96
  return $user;
97
  }
98
 
99
  public function toArray() {
100
  // Note, we do not export "euvatbusiness", since that is only used internally
101
+ // We also do not export "marketingData", since that is handled via CnbAppRemote::enable_email_opt_in/disable_email_opt_in
102
  return array(
103
  'id' => $this->id,
104
  'name' => $this->name,
266
  return $this->toArray();
267
  }
268
  }
269
+
270
+ class CnbUserMarketingData implements JsonSerializable {
271
+
272
+ /**
273
+ * @var string
274
+ */
275
+ public $signupSource;
276
+
277
+ /**
278
+ * @var boolean
279
+ */
280
+ public $emailOptIn = false;
281
+
282
+ /**
283
+ * @var string (date)
284
+ */
285
+ public $emailOptInDate;
286
+
287
+ /**
288
+ * @param $object stdClass|array|WP_Error|null
289
+ *
290
+ * @return CnbUserMarketingData|WP_Error
291
+ */
292
+ public static function fromObject( $object ) {
293
+ if ( is_wp_error( $object ) ) {
294
+ return $object;
295
+ }
296
+ $marketing_data = new CnbUserMarketingData();
297
+ $marketing_data->signupSource = CnbUtils::getPropertyOrNull( $object, 'signupSource' );
298
+ $marketing_data->emailOptIn = CnbUtils::getPropertyOrNull( $object, 'emailOptIn' );
299
+ $marketing_data->emailOptInDate = CnbUtils::getPropertyOrNull( $object, 'emailOptInDate' );
300
+
301
+ return $marketing_data;
302
+ }
303
+
304
+ public function toArray() {
305
+ return array(
306
+ 'signupSource' => $this->signupSource,
307
+ 'emailOptIn' => $this->emailOptIn,
308
+ 'emailOptInDate' => $this->emailOptInDate,
309
+ );
310
+ }
311
+
312
+ public function jsonSerialize() {
313
+ return $this->toArray();
314
+ }
315
+ }
src/admin/partials/CnbFooter.php CHANGED
@@ -5,17 +5,29 @@ namespace cnb;
5
  // don't load directly
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
 
8
  use cnb\admin\api\RemoteTracer;
9
  use cnb\utils\CnbUtils;
10
 
11
  class CnbFooter {
12
- public static function render() {
13
- self::cnb_show_feedback_collection();
14
- self::cnb_show_api_traces();
 
 
 
 
 
 
 
 
 
 
 
15
  echo '</div> <!-- /wrap -->'; // This is started in CnbHeader::
16
  }
17
 
18
- private static function cnb_show_feedback_collection() {
19
  $cnb_options = get_option( 'cnb' );
20
  $cnb_utils = new CnbUtils();
21
 
@@ -49,34 +61,85 @@ class CnbFooter {
49
  <?php
50
  }
51
 
52
- private static function cnb_show_api_traces() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  $cnb_options = get_option( 'cnb' );
54
- if ( isset( $cnb_options['footer_show_traces'] ) && $cnb_options['footer_show_traces'] == 1 &&
55
- isset( $cnb_options['advanced_view'] ) && $cnb_options['advanced_view'] == 1 ) {
 
 
 
 
 
 
 
56
  $cnb_remoted_traces = RemoteTracer::getInstance();
57
- if ( $cnb_remoted_traces ) {
58
- echo '<p>';
59
- $traces = $cnb_remoted_traces->getTraces();
60
- echo '<strong>' . count( $traces ) . '</strong> remote calls executed';
61
- $totaltime = 0.0;
62
- foreach ( $traces as $trace ) {
63
- $totaltime += (float) $trace->getTime();
64
- }
65
- echo ' in <strong>' . esc_html( $totaltime ) . '</strong>sec:<br />';
66
-
67
- echo '<ul>';
68
- foreach ( $traces as $trace ) {
69
- echo '<li>';
70
- echo '<code>' . esc_html( $trace->getEndpoint() ) . '</code> in <strong>' . esc_html( $trace->getTime() ) . '</strong>sec';
71
- if ( $trace->isCacheHit() ) {
72
- echo ' (from cache)';
73
- }
74
- echo '.</li>';
75
- }
76
- echo '</ul>';
77
-
78
- echo '</p>';
79
  }
80
  }
81
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
5
  // don't load directly
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
8
+ use cnb\admin\api\RemoteTrace;
9
  use cnb\admin\api\RemoteTracer;
10
  use cnb\utils\CnbUtils;
11
 
12
  class CnbFooter {
13
+
14
+ /**
15
+ * @var CnbUtils
16
+ */
17
+ private $utils;
18
+
19
+ public function __construct() {
20
+ $this->utils = new CnbUtils();
21
+ }
22
+
23
+ public function render() {
24
+ $this->cnb_show_feedback_collection();
25
+ $this->cnb_show_api_traces();
26
+ $this->add_usage_details();
27
  echo '</div> <!-- /wrap -->'; // This is started in CnbHeader::
28
  }
29
 
30
+ private function cnb_show_feedback_collection() {
31
  $cnb_options = get_option( 'cnb' );
32
  $cnb_utils = new CnbUtils();
33
 
61
  <?php
62
  }
63
 
64
+ /**
65
+ * Error reporting is optional and disabled by default.
66
+ *
67
+ * It needs to be enabled via Settings in order to take effect.
68
+ *
69
+ * This adds some context data for the Error reporting integration to use to collect context
70
+ * in case of an error.
71
+ *
72
+ * @return void
73
+ */
74
+ private function add_usage_details() {
75
+ global $wp_version;
76
+ if ($this->utils->is_reporting_enabled()) {
77
+ echo sprintf('<template
78
+ id="cnb-data"
79
+ data-wordpress-version="%1$s"
80
+ data-wordpress-environment="%2$s"
81
+ data-plugin-version="%3$s"
82
+ "></template>',
83
+ esc_attr($wp_version),
84
+ esc_attr(WP_DEBUG ? 'development' : 'production'),
85
+ esc_attr(CNB_VERSION)
86
+ );
87
+ }
88
+ }
89
+
90
+ public function is_show_traces() {
91
  $cnb_options = get_option( 'cnb' );
92
+ return ! wp_doing_ajax()
93
+ // phpcs:ignore WordPress.Security
94
+ && empty( $_POST )
95
+ && isset( $cnb_options['footer_show_traces'] ) && $cnb_options['footer_show_traces'] == 1
96
+ && isset( $cnb_options['advanced_view'] ) && $cnb_options['advanced_view'] == 1;
97
+ }
98
+
99
+ private function cnb_show_api_traces() {
100
+ if ( $this->is_show_traces() ) {
101
  $cnb_remoted_traces = RemoteTracer::getInstance();
102
+ $traces = $cnb_remoted_traces->getTraces();
103
+ if ( $traces ) {
104
+ $this->print_traces($traces);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  }
106
  }
107
  }
108
+
109
+ /**
110
+ * @param $traces RemoteTrace[]
111
+ *
112
+ * @return void
113
+ */
114
+ public function print_traces($traces) {
115
+ if (!$traces || count($traces) === 0) {
116
+ return;
117
+ }
118
+
119
+ echo '<p>';
120
+ echo '<strong>' . count( $traces ) . '</strong> remote call' . (count( $traces ) !== 1 ? 's' : '') . ' executed';
121
+ $totaltime = 0.0;
122
+ foreach ( $traces as $trace ) {
123
+ $totaltime += (float) $trace->getTime();
124
+ }
125
+ echo ' in <strong>' . esc_html( $totaltime ) . '</strong>sec:<br />';
126
+
127
+ echo '<ul>';
128
+ foreach ( $traces as $trace ) {
129
+ $this->print_trace($trace);
130
+ }
131
+ echo '</ul>';
132
+
133
+ echo '</p>';
134
+ }
135
+
136
+ private function print_trace($trace) {
137
+ echo '<li>';
138
+ echo '<code>' . esc_html( $trace->getEndpoint() ) . '</code> in <strong>' . esc_html( $trace->getTime() ) . '</strong>sec';
139
+ if ( $trace->isCacheHit() ) {
140
+ echo ' (from cache)';
141
+ }
142
+ echo '.</li>';
143
+
144
+ }
145
  }
src/admin/partials/CnbHeader.php CHANGED
@@ -6,8 +6,30 @@ namespace cnb;
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
8
  use cnb\notices\CnbAdminNotices;
 
9
 
10
  class CnbHeader {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  private function preHeader() {
12
  // CSS
13
  wp_enqueue_style( 'wp-color-picker' );
@@ -20,9 +42,13 @@ class CnbHeader {
20
  wp_enqueue_script( 'jquery-ui-dialog' );
21
  wp_enqueue_script( CNB_SLUG . '-call-now-button' );
22
  wp_enqueue_script( CNB_SLUG . '-dismiss' );
 
 
 
23
  }
24
 
25
  private function renderHeader() {
 
26
  echo '<div class="wrap call-now-button-plugin">'; // This is closed in CnbFooter::render
27
 
28
  echo '<!--## NOTIFICATION BARS ## -->';
@@ -39,17 +65,4 @@ class CnbHeader {
39
  // Add the notifications after updating the cloud
40
  CnbAdminNotices::get_instance()->notices( $cnb_cloud_notifications );
41
  }
42
-
43
- public static function render() {
44
- $instance = new CnbHeader();
45
- $instance->preHeader();
46
- $instance->renderHeader();
47
-
48
- do_action( 'cnb_admin_notices' );
49
-
50
- echo '<h1>';
51
- do_action( 'cnb_header_name' );
52
- do_action( 'cnb_after_header' );
53
- echo '</h1>';
54
- }
55
  }
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
8
  use cnb\notices\CnbAdminNotices;
9
+ use cnb\utils\CnbUtils;
10
 
11
  class CnbHeader {
12
+ /**
13
+ * @var CnbUtils
14
+ */
15
+ private $utils;
16
+
17
+ public function __construct() {
18
+ $this->utils = new CnbUtils();
19
+ }
20
+
21
+ public function render() {
22
+ $this->preHeader();
23
+ $this->renderHeader();
24
+
25
+ do_action( 'cnb_admin_notices' );
26
+
27
+ echo '<h1>';
28
+ do_action( 'cnb_header_name' );
29
+ do_action( 'cnb_after_header' );
30
+ echo '</h1>';
31
+ }
32
+
33
  private function preHeader() {
34
  // CSS
35
  wp_enqueue_style( 'wp-color-picker' );
42
  wp_enqueue_script( 'jquery-ui-dialog' );
43
  wp_enqueue_script( CNB_SLUG . '-call-now-button' );
44
  wp_enqueue_script( CNB_SLUG . '-dismiss' );
45
+ if ($this->utils->is_reporting_enabled()) {
46
+ wp_enqueue_script( CNB_SLUG . '-error-reporting' );
47
+ }
48
  }
49
 
50
  private function renderHeader() {
51
+
52
  echo '<div class="wrap call-now-button-plugin">'; // This is closed in CnbFooter::render
53
 
54
  echo '<!--## NOTIFICATION BARS ## -->';
65
  // Add the notifications after updating the cloud
66
  CnbAdminNotices::get_instance()->notices( $cnb_cloud_notifications );
67
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  }
src/admin/partials/CnbHeaderNotices.php CHANGED
@@ -128,7 +128,6 @@ class CnbHeaderNotices {
128
  $privacy_url = $cnb_utils->get_website_url( 'privacy.html', 'email-activation', 'privacy' );
129
  $message = '<form class="cnb-container" onsubmit="return cnb_email_activation_submit()">';
130
  $message .= '<div id="cnb_email_activation_alternate_formd">';
131
- $message .= '<p style="margin-top: 0;"><strong>Email address</strong> (for sending you an activation link):</p>';
132
  $message .= '<input type="text" required="required" class="cnb_activation_input_field" name="cnb_email_activation_alternate_address" id="cnb_email_activation_alternate_address" placeholder="Your email address" /> ';
133
  $message .= get_submit_button( __( 'Activate Premium' ), 'primary', 'cnb_email_activation_alternate', false );
134
  $message .= '</div>';
128
  $privacy_url = $cnb_utils->get_website_url( 'privacy.html', 'email-activation', 'privacy' );
129
  $message = '<form class="cnb-container" onsubmit="return cnb_email_activation_submit()">';
130
  $message .= '<div id="cnb_email_activation_alternate_formd">';
 
131
  $message .= '<input type="text" required="required" class="cnb_activation_input_field" name="cnb_email_activation_alternate_address" id="cnb_email_activation_alternate_address" placeholder="Your email address" /> ';
132
  $message .= get_submit_button( __( 'Activate Premium' ), 'primary', 'cnb_email_activation_alternate', false );
133
  $message .= '</div>';
src/admin/profile/CnbProfileController.php CHANGED
@@ -282,7 +282,8 @@ class CnbProfileController {
282
  return null;
283
  }
284
 
285
- public static function update() {
 
286
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
287
  $page_source = filter_input( INPUT_POST, 'page_source', FILTER_SANITIZE_STRING );
288
  $profile = filter_input(
@@ -292,9 +293,7 @@ class CnbProfileController {
292
  FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
293
  $user = CnbUser::fromObject( $profile );
294
 
295
- $controller = new CnbProfileController();
296
-
297
- $result = $controller->update_user( $nonce, $user );
298
  if ( $result ) {
299
  // Create notification
300
  $notification = array();
@@ -316,8 +315,11 @@ class CnbProfileController {
316
  ),
317
  $url );
318
  $redirect_url = esc_url_raw( $redirect_link );
 
319
  wp_safe_redirect( $redirect_url );
 
320
  } else {
 
321
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
322
  'response' => 403,
323
  'back_link' => true,
282
  return null;
283
  }
284
 
285
+ public function update() {
286
+ do_action( 'cnb_init', __METHOD__ );
287
  $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
288
  $page_source = filter_input( INPUT_POST, 'page_source', FILTER_SANITIZE_STRING );
289
  $profile = filter_input(
293
  FILTER_REQUIRE_ARRAY | FILTER_FLAG_NO_ENCODE_QUOTES );
294
  $user = CnbUser::fromObject( $profile );
295
 
296
+ $result = $this->update_user( $nonce, $user );
 
 
297
  if ( $result ) {
298
  // Create notification
299
  $notification = array();
315
  ),
316
  $url );
317
  $redirect_url = esc_url_raw( $redirect_link );
318
+ do_action( 'cnb_finish' );
319
  wp_safe_redirect( $redirect_url );
320
+ exit;
321
  } else {
322
+ do_action( 'cnb_finish' );
323
  wp_die( esc_html__( 'Invalid nonce specified' ), esc_html__( 'Error' ), array(
324
  'response' => 403,
325
  'back_link' => true,
src/admin/profile/CnbProfileEdit.php CHANGED
@@ -14,13 +14,11 @@ class CnbProfileEdit {
14
  echo 'Profile';
15
  }
16
 
17
- public static function render() {
18
- $view = new CnbProfileEdit();
19
-
20
  wp_enqueue_script( CNB_SLUG . '-profile' );
21
- add_action( 'cnb_header_name', array( $view, 'header' ) );
22
  do_action( 'cnb_header' );
23
- $view->render_form();
24
  do_action( 'cnb_footer' );
25
  }
26
 
14
  echo 'Profile';
15
  }
16
 
17
+ public function render() {
 
 
18
  wp_enqueue_script( CNB_SLUG . '-profile' );
19
+ add_action( 'cnb_header_name', array( $this, 'header' ) );
20
  do_action( 'cnb_header' );
21
+ $this->render_form();
22
  do_action( 'cnb_footer' );
23
  }
24
 
src/admin/profile/CnbProfileRouter.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\admin\profile;
4
+
5
+ // don't load directly
6
+ defined( 'ABSPATH' ) || die( '-1' );
7
+
8
+ class CnbProfileRouter {
9
+ public function render() {
10
+ do_action( 'cnb_init', __METHOD__ );
11
+ (new CnbProfileEdit())->render();
12
+ do_action( 'cnb_finish' );
13
+ }
14
+ }
src/admin/settings/CnbSettingsController.php CHANGED
@@ -24,7 +24,7 @@ class CnbSettingsController {
24
  *
25
  * @return array
26
  */
27
- public static function get_defaults() {
28
  $defaults = array(
29
  'active' => 0,
30
  'number' => '',
@@ -50,7 +50,7 @@ class CnbSettingsController {
50
 
51
  );
52
 
53
- return self::post_option_cnb( $defaults );
54
  }
55
 
56
  /**
@@ -64,7 +64,7 @@ class CnbSettingsController {
64
  *
65
  * @return array
66
  */
67
- public static function post_option_cnb( $cnb_options ) {
68
  $cnb_options['active'] = isset( $cnb_options['active'] ) && $cnb_options['active'] == 1 ? 1 : 0;
69
  $cnb_options['hideIcon'] = isset( $cnb_options['hideIcon'] ) && $cnb_options['hideIcon'] == 1 ? 1 : 0;
70
  $cnb_options['frontpage'] = isset( $cnb_options['frontpage'] ) && $cnb_options['frontpage'] == 1 ? 1 : 0;
@@ -78,6 +78,29 @@ class CnbSettingsController {
78
  return $cnb_options;
79
  }
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  /**
82
  * @param $cnb_options array
83
  *
@@ -93,7 +116,7 @@ class CnbSettingsController {
93
  *
94
  * @return array The adjusted options array for Call Now Button
95
  */
96
- public static function validate_options( $input ) {
97
  $original_settings = get_option( 'cnb' );
98
 
99
  $messages = array();
@@ -155,7 +178,7 @@ class CnbSettingsController {
155
  $version_upgrade = $original_settings['version'] != $updated_options['version'] || $original_settings['changelog_version'] != $updated_options['changelog_version'];
156
 
157
  // Check for legacy button
158
- $check = self::disallow_active_without_phone_number( $updated_options );
159
 
160
  if ( is_wp_error( $check ) ) {
161
  if ( $check->get_error_code() === 'CNB_PHONE_NUMBER_MISSING' ) {
@@ -174,18 +197,36 @@ class CnbSettingsController {
174
  $messages[] = new CnbNotice( 'success', '<p>Your settings have been updated!</p>' );
175
  }
176
 
 
177
  $transient_id = 'cnb-options';
178
  $messages = apply_filters( 'cnb_after_save', $messages );
179
  set_transient( $transient_id, $messages, HOUR_IN_SECONDS );
180
 
181
  // We do not actually store this value in the DB!
182
  unset( $updated_options['status'] );
 
183
 
184
- do_action('cnb_after_button_changed');
185
 
186
  return $updated_options;
187
  }
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  /**
190
  * For the Legacy button, disallow setting it to active with a missing phone number
191
  *
@@ -193,7 +234,7 @@ class CnbSettingsController {
193
  *
194
  * @return WP_Error
195
  */
196
- private static function disallow_active_without_phone_number( $input ) {
197
  $number = trim( $input['number'] );
198
  $cloud_enabled = array_key_exists( 'cloud_enabled', $input ) ? $input['cloud_enabled'] : 0;
199
  if ( $input['active'] == 1 && $cloud_enabled == 0 && empty( $number ) ) {
24
  *
25
  * @return array
26
  */
27
+ public function get_defaults() {
28
  $defaults = array(
29
  'active' => 0,
30
  'number' => '',
50
 
51
  );
52
 
53
+ return $this->post_option_cnb( $defaults );
54
  }
55
 
56
  /**
64
  *
65
  * @return array
66
  */
67
+ public function post_option_cnb( $cnb_options ) {
68
  $cnb_options['active'] = isset( $cnb_options['active'] ) && $cnb_options['active'] == 1 ? 1 : 0;
69
  $cnb_options['hideIcon'] = isset( $cnb_options['hideIcon'] ) && $cnb_options['hideIcon'] == 1 ? 1 : 0;
70
  $cnb_options['frontpage'] = isset( $cnb_options['frontpage'] ) && $cnb_options['frontpage'] == 1 ? 1 : 0;
78
  return $cnb_options;
79
  }
80
 
81
+
82
+ /**
83
+ * After the upgrade (changelog) dialog is dismissed, the "cnb_update_<version>" action is fired.
84
+ *
85
+ * This action updates the internal version number, so we're ready for the next update.
86
+ *
87
+ * @return void
88
+ */
89
+ public function update_version() {
90
+ $this->set_changelog_version();
91
+ }
92
+
93
+ private function set_changelog_version() {
94
+ $cnb_options = get_option( 'cnb' );
95
+ $updated_options = array_merge(
96
+ $cnb_options,
97
+ array(
98
+ 'changelog_version' => CNB_VERSION
99
+ )
100
+ );
101
+ update_option( 'cnb', $updated_options );
102
+ }
103
+
104
  /**
105
  * @param $cnb_options array
106
  *
116
  *
117
  * @return array The adjusted options array for Call Now Button
118
  */
119
+ public function validate_options( $input ) {
120
  $original_settings = get_option( 'cnb' );
121
 
122
  $messages = array();
178
  $version_upgrade = $original_settings['version'] != $updated_options['version'] || $original_settings['changelog_version'] != $updated_options['changelog_version'];
179
 
180
  // Check for legacy button
181
+ $check = $this->disallow_active_without_phone_number( $updated_options );
182
 
183
  if ( is_wp_error( $check ) ) {
184
  if ( $check->get_error_code() === 'CNB_PHONE_NUMBER_MISSING' ) {
197
  $messages[] = new CnbNotice( 'success', '<p>Your settings have been updated!</p>' );
198
  }
199
 
200
+ $this->update_user_email_opt_in($original_settings, $input);
201
  $transient_id = 'cnb-options';
202
  $messages = apply_filters( 'cnb_after_save', $messages );
203
  set_transient( $transient_id, $messages, HOUR_IN_SECONDS );
204
 
205
  // We do not actually store this value in the DB!
206
  unset( $updated_options['status'] );
207
+ unset( $updated_options['user_marketing_email_opt_in'] );
208
 
209
+ do_action( 'cnb_after_button_changed' );
210
 
211
  return $updated_options;
212
  }
213
 
214
+ private function update_user_email_opt_in($current, $new) {
215
+ if (!key_exists('user_marketing_email_opt_in', $new)) {
216
+ return;
217
+ }
218
+
219
+ // Test if properties are there (if not in current or current != new, update CnbUser
220
+ if (!key_exists('user_marketing_email_opt_in', $current) || $current['user_marketing_email_opt_in'] != $new['user_marketing_email_opt_in']) {
221
+ // phpcs:ignore PHPCompatibility.FunctionUse
222
+ if (boolval($new['user_marketing_email_opt_in'])) {
223
+ CnbAppRemote::enable_email_opt_in();
224
+ } else {
225
+ CnbAppRemote::disable_email_opt_in();
226
+ }
227
+ }
228
+ }
229
+
230
  /**
231
  * For the Legacy button, disallow setting it to active with a missing phone number
232
  *
234
  *
235
  * @return WP_Error
236
  */
237
+ private function disallow_active_without_phone_number( $input ) {
238
  $number = trim( $input['number'] );
239
  $cloud_enabled = array_key_exists( 'cloud_enabled', $input ) ? $input['cloud_enabled'] : 0;
240
  if ( $input['active'] == 1 && $cloud_enabled == 0 && empty( $number ) ) {
src/admin/settings/CnbSettingsRouter.php CHANGED
@@ -12,7 +12,8 @@ class CnbSettingsRouter {
12
  *
13
  * @return void
14
  */
15
- public static function render() {
 
16
  $controller = new CnbSettingsController();
17
 
18
  $activation = $controller->parseApiAndOttHeader();
@@ -27,5 +28,6 @@ class CnbSettingsRouter {
27
 
28
  $view = new CnbSettingsViewEdit();
29
  $view->render();
 
30
  }
31
  }
12
  *
13
  * @return void
14
  */
15
+ public function render() {
16
+ do_action( 'cnb_init', __METHOD__ );
17
  $controller = new CnbSettingsController();
18
 
19
  $activation = $controller->parseApiAndOttHeader();
28
 
29
  $view = new CnbSettingsViewEdit();
30
  $view->render();
31
+ do_action( 'cnb_finish' );
32
  }
33
  }
src/admin/settings/CnbSettingsViewEdit.php CHANGED
@@ -54,12 +54,37 @@ class CnbSettingsViewEdit {
54
  $view->render_zindex();
55
  }
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  /**
58
  * @param $cnb_user CnbUser
 
59
  *
60
  * @return void
61
  */
62
- private function render_account_options( $cnb_user ) {
63
  global $wp_version;
64
  $cnb_options = get_option( 'cnb' );
65
  $show_advanced_view_only = array_key_exists( 'advanced_view', $cnb_options ) && $cnb_options['advanced_view'] === 1;
@@ -72,6 +97,49 @@ class CnbSettingsViewEdit {
72
  <tr>
73
  <th colspan="2"></th>
74
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  <tr>
76
  <th scope="row">API key</th>
77
  <td>
@@ -103,38 +171,19 @@ class CnbSettingsViewEdit {
103
  $icon = version_compare( $wp_version, '5.5.0', '<' ) ? 'dashicons-yes' : 'dashicons-saved';
104
  ?>
105
  <p><strong><span class="dashicons <?php echo esc_attr( $icon ) ?>"></span>Success!</strong>
106
- <Br>The plugin is connected to your callnowbutton.com account.</p>
107
- <p class="description">Clicking "Disconnect account" will drop the API key
108
- and disconnect the plugin from your account. You will lose access to
109
- your buttons and Premium functionality until you reconnect with a
110
- callnowbutton.com account.
111
- <br>
112
  <input type="button" name="cnb_api_key_delete" id="cnb_api_key_delete"
113
- class="button button-link"
114
  value="<?php esc_attr_e( 'Disconnect account' ) ?>"
115
- onclick="return cnb_delete_apikey();">
116
- </p>
117
- <input type="hidden" name="cnb[api_key]" id="cnb_api_key" value="delete_me"
118
- disabled="disabled"/>
 
119
  <?php } ?>
120
  </td>
121
  </tr>
122
- <?php if ( $cnb_user !== null && ! $cnb_user instanceof WP_Error ) { ?>
123
- <tr>
124
- <th scope="row">Account owner</th>
125
- <td>
126
- <?php echo esc_html( $cnb_user->name ) ?>
127
- <?php
128
- if ( $cnb_user->email !== $cnb_user->name ) {
129
- echo esc_html( ' (' . $cnb_user->email . ')' );
130
- } ?>
131
- </td>
132
- </tr>
133
- <tr>
134
- <th scope="row">Account ID</th>
135
- <td><code><?php echo esc_html( $cnb_user->id ) ?></code></td>
136
- </tr>
137
- <?php } ?>
138
  </table>
139
 
140
  <?php
@@ -308,7 +357,7 @@ class CnbSettingsViewEdit {
308
  '<p>You can even replace it with an email button during your off-hours so people can still contact you.</p>',
309
  'clock',
310
  '<strong>Use the scheduler!</strong>',
311
- 'Learn more',
312
  ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
313
  );
314
  ( new CnbAdminFunctions() )->cnb_promobox(
@@ -336,12 +385,12 @@ class CnbSettingsViewEdit {
336
  if ( $use_cloud && isset( $cnb_cloud_domain ) && ! is_wp_error( $cnb_cloud_domain ) && $cnb_cloud_domain->type !== 'PRO' ) {
337
  ( new CnbAdminFunctions() )->cnb_promobox(
338
  'purple',
339
- '50% off the annual plan!',
340
  '<p>Remove the <em>powered by</em> branding from your buttons!</p>' .
341
- '<p>Benefit from this temporary offer and enjoy unlimited access to all features and publish your buttons without branding.</p>',
342
  'flag',
343
- '<strong>&euro;<span class="eur-per-month"></span>/$<span class="usd-per-month"></span> per month</strong>',
344
- 'Upgrade',
345
  ( new CnbUtils() )->get_cnb_domain_upgrade( $cnb_cloud_domain )
346
  );
347
  }
@@ -406,6 +455,7 @@ class CnbSettingsViewEdit {
406
 
407
  wp_enqueue_script( CNB_SLUG . '-settings' );
408
  wp_enqueue_script( CNB_SLUG . '-timezone-picker-fix' );
 
409
 
410
  add_action( 'cnb_header_name', array( $this, 'header' ) );
411
 
@@ -470,10 +520,14 @@ class CnbSettingsViewEdit {
470
  $domain_edit->render_form_plan_details( $cnb_cloud_domain );
471
  $domain_edit->render_form_tracking( $cnb_cloud_domain );
472
  $domain_edit->render_form_button_display( $cnb_cloud_domain );
473
- } ?>
 
 
 
 
474
  </table>
475
  <?php if ( $status === 'cloud' ) {
476
- $this->render_account_options( $cnb_user );
477
  $this->render_advanced_options( $cnb_cloud_domain, $cnb_user );
478
  }
479
  ?>
54
  $view->render_zindex();
55
  }
56
 
57
+ private function render_error_reporting_options() {
58
+ $cnb_options = get_option('cnb');
59
+ ?>
60
+ <tr>
61
+ <th colspan="2"><h2>Miscellaneous</h2></th>
62
+ </tr>
63
+ <tr>
64
+ <th>Errors and usage</th>
65
+ <td>
66
+ <input type="hidden" name="cnb[error_reporting]" value="0"/>
67
+ <input id="cnb-error-reporting" class="cnb_toggle_checkbox" type="checkbox"
68
+ name="cnb[error_reporting]"
69
+ value="1" <?php checked( '1', $cnb_options['error_reporting'] ); ?> />
70
+ <label for="cnb-error-reporting" class="cnb_toggle_label">Toggle</label>
71
+ <span data-cnb_toggle_state_label="cnb-error-reporting"
72
+ class="cnb_toggle_state cnb_toggle_false">(Not sharing)</span>
73
+ <span data-cnb_toggle_state_label="cnb-error-reporting"
74
+ class="cnb_toggle_state cnb_toggle_true">Share</span>
75
+ <p class="description">Allows us to capture anonymous error reports and usage statistics to help us improve the product.</p>
76
+ </td>
77
+ </tr>
78
+ <?php
79
+ }
80
+
81
  /**
82
  * @param $cnb_user CnbUser
83
+ * @param $cnb_cloud_domain CnbDomain
84
  *
85
  * @return void
86
  */
87
+ private function render_account_options( $cnb_user, $cnb_cloud_domain ) {
88
  global $wp_version;
89
  $cnb_options = get_option( 'cnb' );
90
  $show_advanced_view_only = array_key_exists( 'advanced_view', $cnb_options ) && $cnb_options['advanced_view'] === 1;
97
  <tr>
98
  <th colspan="2"></th>
99
  </tr>
100
+
101
+ <?php if ( $cnb_user !== null && ! $cnb_user instanceof WP_Error ) { ?>
102
+ <tr>
103
+ <th scope="row">Account owner</th>
104
+ <td>
105
+ <?php echo esc_html( $cnb_user->name ) ?>
106
+ <?php
107
+ if ( $cnb_user->email !== $cnb_user->name ) {
108
+ echo esc_html( ' (' . $cnb_user->email . ')' );
109
+ } ?>
110
+ </td>
111
+ </tr>
112
+ <tr>
113
+ <th scope="row">Account ID</th>
114
+ <td><code id="cnb_user_id"><?php echo esc_html( $cnb_user->id ) ?></code></td>
115
+ </tr>
116
+ <?php
117
+ if ( $cnb_options['cloud_enabled'] == 1 && !is_wp_error($cnb_cloud_domain) && $cnb_cloud_domain->type === 'PRO' ) {
118
+ $stripe_link = CnbAppRemote::cnb_remote_create_billing_portal();
119
+ if (!is_wp_error($stripe_link)) {
120
+ ?>
121
+ <tr>
122
+ <th scope="row">Invoices</th>
123
+ <td><a href="<? echo esc_url($stripe_link->url) ?>" target="_blank">Billing portal</a></td>
124
+ </tr>
125
+ <?php } ?>
126
+ <?php } ?>
127
+ <tr>
128
+ <th>Product updates</th>
129
+ <td>
130
+ <input type="hidden" name="cnb[user_marketing_email_opt_in]" value="0"/>
131
+ <input id="cnb_user_marketing_email_opt_in" class="cnb_toggle_checkbox" name="cnb[user_marketing_email_opt_in]"
132
+ type="checkbox"
133
+ value="1" <?php checked( $cnb_user->marketingData->emailOptIn ); ?> />
134
+ <label for="cnb_user_marketing_email_opt_in" class="cnb_toggle_label">Receive e-mail</label>
135
+ <span data-cnb_toggle_state_label="cnb_user_marketing_email_opt_in"
136
+ class="cnb_toggle_state cnb_toggle_false">(Disabled)</span>
137
+ <span data-cnb_toggle_state_label="user_marketing_email_opt_in"
138
+ class="cnb_toggle_state cnb_toggle_true">Enabled</span>
139
+ <p class="description">Receive email updates on new features we're adding and how to use them.</p>
140
+ </td>
141
+ </tr>
142
+ <?php } ?>
143
  <tr>
144
  <th scope="row">API key</th>
145
  <td>
171
  $icon = version_compare( $wp_version, '5.5.0', '<' ) ? 'dashicons-yes' : 'dashicons-saved';
172
  ?>
173
  <p><strong><span class="dashicons <?php echo esc_attr( $icon ) ?>"></span>Success!</strong>
174
+ The plugin is connected to your callnowbutton.com account.</p>
175
+ <p>
 
 
 
 
176
  <input type="button" name="cnb_api_key_delete" id="cnb_api_key_delete"
177
+ class="button button-secondary"
178
  value="<?php esc_attr_e( 'Disconnect account' ) ?>"
179
+ onclick="return cnb_delete_apikey();"> </p>
180
+ <p class="description">Clicking "Disconnect account" will drop the API key and disconnect the plugin from your account. You will lose access to your buttons and Premium functionality until you reconnect with a callnowbutton.com account.</p>
181
+
182
+
183
+ <input type="hidden" name="cnb[api_key]" id="cnb_api_key" value="delete_me" disabled="disabled"/>
184
  <?php } ?>
185
  </td>
186
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  </table>
188
 
189
  <?php
357
  '<p>You can even replace it with an email button during your off-hours so people can still contact you.</p>',
358
  'clock',
359
  '<strong>Use the scheduler!</strong>',
360
+ 'Get Premium',
361
  ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
362
  );
363
  ( new CnbAdminFunctions() )->cnb_promobox(
385
  if ( $use_cloud && isset( $cnb_cloud_domain ) && ! is_wp_error( $cnb_cloud_domain ) && $cnb_cloud_domain->type !== 'PRO' ) {
386
  ( new CnbAdminFunctions() )->cnb_promobox(
387
  'purple',
388
+ 'Remove Branding with PRO!',
389
  '<p>Remove the <em>powered by</em> branding from your buttons!</p>' .
390
+ '<p>Enjoy unlimited access to all features and publish your buttons without branding.</p>',
391
  'flag',
392
+ '<strong>&euro;<span class="eur-per-month"></span>/$<span class="usd-per-month"></span> monthly</strong>',
393
+ 'Upgrade now',
394
  ( new CnbUtils() )->get_cnb_domain_upgrade( $cnb_cloud_domain )
395
  );
396
  }
455
 
456
  wp_enqueue_script( CNB_SLUG . '-settings' );
457
  wp_enqueue_script( CNB_SLUG . '-timezone-picker-fix' );
458
+ wp_enqueue_script( CNB_SLUG . '-tally' );
459
 
460
  add_action( 'cnb_header_name', array( $this, 'header' ) );
461
 
520
  $domain_edit->render_form_plan_details( $cnb_cloud_domain );
521
  $domain_edit->render_form_tracking( $cnb_cloud_domain );
522
  $domain_edit->render_form_button_display( $cnb_cloud_domain );
523
+ }
524
+
525
+ $this->render_error_reporting_options();
526
+
527
+ ?>
528
  </table>
529
  <?php if ( $status === 'cloud' ) {
530
+ $this->render_account_options( $cnb_user, $cnb_cloud_domain );
531
  $this->render_advanced_options( $cnb_cloud_domain, $cnb_user );
532
  }
533
  ?>
src/admin/settings/StripeBillingPortal.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\admin\settings;
4
+
5
+ // don't load directly
6
+ defined( 'ABSPATH' ) || die( '-1' );
7
+
8
+ use cnb\utils\CnbUtils;
9
+
10
+ class StripeBillingPortal {
11
+ /**
12
+ * @var string
13
+ */
14
+ public $url;
15
+
16
+ public static function fromObject( $object ) {
17
+ if ( is_wp_error( $object ) ) {
18
+ return $object;
19
+ }
20
+
21
+ $portal = new StripeBillingPortal();
22
+ $portal->url = CnbUtils::getPropertyOrNull( $object, 'url' );
23
+
24
+ return $portal;
25
+ }
26
+ }
src/autoload.php CHANGED
@@ -36,6 +36,7 @@ spl_autoload_register(
36
  'cnb\\admin\\button\\cnbbuttonview' => '/admin/button/CnbButtonView.php',
37
  'cnb\\admin\\button\\cnbbuttonviewedit' => '/admin/button/CnbButtonViewEdit.php',
38
  'cnb\\admin\\button\\cnbmultibuttonoptions' => '/admin/button/CnbButton.php',
 
39
  'cnb\\admin\\cnbadminajax' => '/admin/CnbAdminAjax.php',
40
  'cnb\\admin\\condition\\cnb_condition_list_table' => '/admin/condition/Cnb_Condition_List_Table.php',
41
  'cnb\\admin\\condition\\cnbcondition' => '/admin/condition/CnbCondition.php',
@@ -43,6 +44,7 @@ spl_autoload_register(
43
  'cnb\\admin\\condition\\cnbconditionrouter' => '/admin/condition/CnbConditionRouter.php',
44
  'cnb\\admin\\condition\\cnbconditionview' => '/admin/condition/CnbConditionView.php',
45
  'cnb\\admin\\condition\\cnbconditionviewedit' => '/admin/condition/CnbConditionViewEdit.php',
 
46
  'cnb\\admin\\domain\\cnb_domain_list_table' => '/admin/domain/Cnb_Domain_List_Table.php',
47
  'cnb\\admin\\domain\\cnbdomain' => '/admin/domain/CnbDomain.php',
48
  'cnb\\admin\\domain\\cnbdomaincontroller' => '/admin/domain/CnbDomainController.php',
@@ -61,15 +63,18 @@ spl_autoload_register(
61
  'cnb\\admin\\models\\cnbplan' => '/admin/models/CnbPlan.php',
62
  'cnb\\admin\\models\\cnbuser' => '/admin/models/CnbUser.php',
63
  'cnb\\admin\\models\\cnbuseraddress' => '/admin/models/CnbUser.php',
 
64
  'cnb\\admin\\models\\cnbuserstripedetails' => '/admin/models/CnbUser.php',
65
  'cnb\\admin\\models\\cnbusertaxid' => '/admin/models/CnbUser.php',
66
  'cnb\\admin\\models\\cnbusertaxidverification' => '/admin/models/CnbUser.php',
67
  'cnb\\admin\\profile\\cnbprofilecontroller' => '/admin/profile/CnbProfileController.php',
68
  'cnb\\admin\\profile\\cnbprofileedit' => '/admin/profile/CnbProfileEdit.php',
 
69
  'cnb\\admin\\settings\\cnbapikeyactivatedview' => '/admin/settings/CnbApiKeyActivatedView.php',
70
  'cnb\\admin\\settings\\cnbsettingscontroller' => '/admin/settings/CnbSettingsController.php',
71
  'cnb\\admin\\settings\\cnbsettingsrouter' => '/admin/settings/CnbSettingsRouter.php',
72
  'cnb\\admin\\settings\\cnbsettingsviewedit' => '/admin/settings/CnbSettingsViewEdit.php',
 
73
  'cnb\\cache\\cachehandler' => '/utils/class-cachehandler.php',
74
  'cnb\\callnowbutton' => '/CallNowButton.php',
75
  'cnb\\cli\\cnb_cli' => '/cli/CNB_CLI.php',
@@ -87,6 +92,7 @@ spl_autoload_register(
87
  'cnb\\renderer\\nooprenderer' => '/renderers/noop/class-nooprenderer.php',
88
  'cnb\\renderer\\renderer' => '/renderers/class-renderer.php',
89
  'cnb\\renderer\\rendererfactory' => '/renderers/class-rendererfactory.php',
 
90
  'cnb\\utils\\cnbadminfunctions' => '/utils/CnbAdminFunctions.php',
91
  'cnb\\utils\\cnbutils' => '/utils/CnbUtils.php',
92
  'wp_cli' => '/cli/mocks/WP_CLI.class.php',
36
  'cnb\\admin\\button\\cnbbuttonview' => '/admin/button/CnbButtonView.php',
37
  'cnb\\admin\\button\\cnbbuttonviewedit' => '/admin/button/CnbButtonViewEdit.php',
38
  'cnb\\admin\\button\\cnbmultibuttonoptions' => '/admin/button/CnbButton.php',
39
+ 'cnb\\admin\\button\\cnbscrolloptions' => '/admin/button/CnbButton.php',
40
  'cnb\\admin\\cnbadminajax' => '/admin/CnbAdminAjax.php',
41
  'cnb\\admin\\condition\\cnb_condition_list_table' => '/admin/condition/Cnb_Condition_List_Table.php',
42
  'cnb\\admin\\condition\\cnbcondition' => '/admin/condition/CnbCondition.php',
44
  'cnb\\admin\\condition\\cnbconditionrouter' => '/admin/condition/CnbConditionRouter.php',
45
  'cnb\\admin\\condition\\cnbconditionview' => '/admin/condition/CnbConditionView.php',
46
  'cnb\\admin\\condition\\cnbconditionviewedit' => '/admin/condition/CnbConditionViewEdit.php',
47
+ 'cnb\\admin\\deactivation\\deactivation' => '/admin/deactivation/Deactivation.php',
48
  'cnb\\admin\\domain\\cnb_domain_list_table' => '/admin/domain/Cnb_Domain_List_Table.php',
49
  'cnb\\admin\\domain\\cnbdomain' => '/admin/domain/CnbDomain.php',
50
  'cnb\\admin\\domain\\cnbdomaincontroller' => '/admin/domain/CnbDomainController.php',
63
  'cnb\\admin\\models\\cnbplan' => '/admin/models/CnbPlan.php',
64
  'cnb\\admin\\models\\cnbuser' => '/admin/models/CnbUser.php',
65
  'cnb\\admin\\models\\cnbuseraddress' => '/admin/models/CnbUser.php',
66
+ 'cnb\\admin\\models\\cnbusermarketingdata' => '/admin/models/CnbUser.php',
67
  'cnb\\admin\\models\\cnbuserstripedetails' => '/admin/models/CnbUser.php',
68
  'cnb\\admin\\models\\cnbusertaxid' => '/admin/models/CnbUser.php',
69
  'cnb\\admin\\models\\cnbusertaxidverification' => '/admin/models/CnbUser.php',
70
  'cnb\\admin\\profile\\cnbprofilecontroller' => '/admin/profile/CnbProfileController.php',
71
  'cnb\\admin\\profile\\cnbprofileedit' => '/admin/profile/CnbProfileEdit.php',
72
+ 'cnb\\admin\\profile\\cnbprofilerouter' => '/admin/profile/CnbProfileRouter.php',
73
  'cnb\\admin\\settings\\cnbapikeyactivatedview' => '/admin/settings/CnbApiKeyActivatedView.php',
74
  'cnb\\admin\\settings\\cnbsettingscontroller' => '/admin/settings/CnbSettingsController.php',
75
  'cnb\\admin\\settings\\cnbsettingsrouter' => '/admin/settings/CnbSettingsRouter.php',
76
  'cnb\\admin\\settings\\cnbsettingsviewedit' => '/admin/settings/CnbSettingsViewEdit.php',
77
+ 'cnb\\admin\\settings\\stripebillingportal' => '/admin/settings/StripeBillingPortal.php',
78
  'cnb\\cache\\cachehandler' => '/utils/class-cachehandler.php',
79
  'cnb\\callnowbutton' => '/CallNowButton.php',
80
  'cnb\\cli\\cnb_cli' => '/cli/CNB_CLI.php',
92
  'cnb\\renderer\\nooprenderer' => '/renderers/noop/class-nooprenderer.php',
93
  'cnb\\renderer\\renderer' => '/renderers/class-renderer.php',
94
  'cnb\\renderer\\rendererfactory' => '/renderers/class-rendererfactory.php',
95
+ 'cnb\\utils\\cnb_sentry' => '/utils/class-cnb-sentry.php',
96
  'cnb\\utils\\cnbadminfunctions' => '/utils/CnbAdminFunctions.php',
97
  'cnb\\utils\\cnbutils' => '/utils/CnbUtils.php',
98
  'wp_cli' => '/cli/mocks/WP_CLI.class.php',
src/call-now-button.php CHANGED
@@ -1,6 +1,8 @@
1
  <?php
2
 
3
  // don't load directly
 
 
4
  defined( 'ABSPATH' ) || die( '-1' );
5
 
6
  require_once dirname( __FILE__ ) . '/autoload.php';
@@ -11,13 +13,17 @@ if ( class_exists( 'WP_CLI' ) && class_exists( 'WP_CLI_Command' ) ) {
11
  require_once dirname( __FILE__ ) . '/cli/CNB_CLI.php';
12
  }
13
 
14
- add_action( 'plugins_loaded', array( 'cnb\CallNowButton', 'registerGlobalActions' ) );
15
- add_action( 'plugins_loaded', array( 'cnb\CallNowButton', 'registerHeaderAndFooter' ) );
16
- add_action( 'plugins_loaded', array( 'cnb\CallNowButton', 'registerPostActions' ) );
17
- add_action( 'plugins_loaded', array( 'cnb\CallNowButton', 'registerAjax' ) );
 
 
18
 
19
- // Ensure we are excluded from certain Caching plugins
20
- add_action( 'plugins_loaded', array( 'cnb\cache\CacheHandler', 'exclude' ) );
21
 
22
- // This queues the front-end to be rendered (`wp_loaded` should only fire on the front-end facing site)
23
- add_action( 'wp_loaded', array( 'cnb\renderer\RendererFactory', 'register' ) );
 
 
1
  <?php
2
 
3
  // don't load directly
4
+ use cnb\CallNowButton;
5
+
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
8
  require_once dirname( __FILE__ ) . '/autoload.php';
13
  require_once dirname( __FILE__ ) . '/cli/CNB_CLI.php';
14
  }
15
 
16
+ function cnb_add_actions() {
17
+ $call_now_button = new CallNowButton();
18
+ add_action( 'plugins_loaded', array( $call_now_button, 'register_global_actions' ) );
19
+ add_action( 'plugins_loaded', array( $call_now_button, 'register_header_and_footer' ) );
20
+ add_action( 'plugins_loaded', array( $call_now_button, 'register_admin_post_actions' ) );
21
+ add_action( 'plugins_loaded', array( $call_now_button, 'register_ajax_actions' ) );
22
 
23
+ // Ensure we are excluded from certain Caching plugins
24
+ add_action( 'plugins_loaded', array( 'cnb\cache\CacheHandler', 'exclude' ) );
25
 
26
+ // This queues the front-end to be rendered (`wp_loaded` should only fire on the front-end facing site)
27
+ add_action( 'wp_loaded', array( 'cnb\renderer\RendererFactory', 'register' ) );
28
+ }
29
+ cnb_add_actions();
src/composer.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ {
2
+ "require": {
3
+ "php": "^7.2|^8.0",
4
+ "sentry/sdk": "3.2.0"
5
+ }
6
+ }
src/composer.lock ADDED
@@ -0,0 +1,1884 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_readme": [
3
+ "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
+ "This file is @generated automatically"
6
+ ],
7
+ "content-hash": "7bf6e13ecfe6677f5e64a1632bf3f99c",
8
+ "packages": [
9
+ {
10
+ "name": "clue/stream-filter",
11
+ "version": "v1.6.0",
12
+ "source": {
13
+ "type": "git",
14
+ "url": "https://github.com/clue/stream-filter.git",
15
+ "reference": "d6169430c7731d8509da7aecd0af756a5747b78e"
16
+ },
17
+ "dist": {
18
+ "type": "zip",
19
+ "url": "https://api.github.com/repos/clue/stream-filter/zipball/d6169430c7731d8509da7aecd0af756a5747b78e",
20
+ "reference": "d6169430c7731d8509da7aecd0af756a5747b78e",
21
+ "shasum": ""
22
+ },
23
+ "require": {
24
+ "php": ">=5.3"
25
+ },
26
+ "require-dev": {
27
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36"
28
+ },
29
+ "type": "library",
30
+ "autoload": {
31
+ "files": [
32
+ "src/functions_include.php"
33
+ ],
34
+ "psr-4": {
35
+ "Clue\\StreamFilter\\": "src/"
36
+ }
37
+ },
38
+ "notification-url": "https://packagist.org/downloads/",
39
+ "license": [
40
+ "MIT"
41
+ ],
42
+ "authors": [
43
+ {
44
+ "name": "Christian Lück",
45
+ "email": "christian@clue.engineering"
46
+ }
47
+ ],
48
+ "description": "A simple and modern approach to stream filtering in PHP",
49
+ "homepage": "https://github.com/clue/php-stream-filter",
50
+ "keywords": [
51
+ "bucket brigade",
52
+ "callback",
53
+ "filter",
54
+ "php_user_filter",
55
+ "stream",
56
+ "stream_filter_append",
57
+ "stream_filter_register"
58
+ ],
59
+ "support": {
60
+ "issues": "https://github.com/clue/stream-filter/issues",
61
+ "source": "https://github.com/clue/stream-filter/tree/v1.6.0"
62
+ },
63
+ "funding": [
64
+ {
65
+ "url": "https://clue.engineering/support",
66
+ "type": "custom"
67
+ },
68
+ {
69
+ "url": "https://github.com/clue",
70
+ "type": "github"
71
+ }
72
+ ],
73
+ "time": "2022-02-21T13:15:14+00:00"
74
+ },
75
+ {
76
+ "name": "guzzlehttp/promises",
77
+ "version": "1.5.1",
78
+ "source": {
79
+ "type": "git",
80
+ "url": "https://github.com/guzzle/promises.git",
81
+ "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
82
+ },
83
+ "dist": {
84
+ "type": "zip",
85
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
86
+ "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
87
+ "shasum": ""
88
+ },
89
+ "require": {
90
+ "php": ">=5.5"
91
+ },
92
+ "require-dev": {
93
+ "symfony/phpunit-bridge": "^4.4 || ^5.1"
94
+ },
95
+ "type": "library",
96
+ "extra": {
97
+ "branch-alias": {
98
+ "dev-master": "1.5-dev"
99
+ }
100
+ },
101
+ "autoload": {
102
+ "files": [
103
+ "src/functions_include.php"
104
+ ],
105
+ "psr-4": {
106
+ "GuzzleHttp\\Promise\\": "src/"
107
+ }
108
+ },
109
+ "notification-url": "https://packagist.org/downloads/",
110
+ "license": [
111
+ "MIT"
112
+ ],
113
+ "authors": [
114
+ {
115
+ "name": "Graham Campbell",
116
+ "email": "hello@gjcampbell.co.uk",
117
+ "homepage": "https://github.com/GrahamCampbell"
118
+ },
119
+ {
120
+ "name": "Michael Dowling",
121
+ "email": "mtdowling@gmail.com",
122
+ "homepage": "https://github.com/mtdowling"
123
+ },
124
+ {
125
+ "name": "Tobias Nyholm",
126
+ "email": "tobias.nyholm@gmail.com",
127
+ "homepage": "https://github.com/Nyholm"
128
+ },
129
+ {
130
+ "name": "Tobias Schultze",
131
+ "email": "webmaster@tubo-world.de",
132
+ "homepage": "https://github.com/Tobion"
133
+ }
134
+ ],
135
+ "description": "Guzzle promises library",
136
+ "keywords": [
137
+ "promise"
138
+ ],
139
+ "support": {
140
+ "issues": "https://github.com/guzzle/promises/issues",
141
+ "source": "https://github.com/guzzle/promises/tree/1.5.1"
142
+ },
143
+ "funding": [
144
+ {
145
+ "url": "https://github.com/GrahamCampbell",
146
+ "type": "github"
147
+ },
148
+ {
149
+ "url": "https://github.com/Nyholm",
150
+ "type": "github"
151
+ },
152
+ {
153
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
154
+ "type": "tidelift"
155
+ }
156
+ ],
157
+ "time": "2021-10-22T20:56:57+00:00"
158
+ },
159
+ {
160
+ "name": "guzzlehttp/psr7",
161
+ "version": "2.3.0",
162
+ "source": {
163
+ "type": "git",
164
+ "url": "https://github.com/guzzle/psr7.git",
165
+ "reference": "83260bb50b8fc753c72d14dc1621a2dac31877ee"
166
+ },
167
+ "dist": {
168
+ "type": "zip",
169
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/83260bb50b8fc753c72d14dc1621a2dac31877ee",
170
+ "reference": "83260bb50b8fc753c72d14dc1621a2dac31877ee",
171
+ "shasum": ""
172
+ },
173
+ "require": {
174
+ "php": "^7.2.5 || ^8.0",
175
+ "psr/http-factory": "^1.0",
176
+ "psr/http-message": "^1.0",
177
+ "ralouphie/getallheaders": "^3.0"
178
+ },
179
+ "provide": {
180
+ "psr/http-factory-implementation": "1.0",
181
+ "psr/http-message-implementation": "1.0"
182
+ },
183
+ "require-dev": {
184
+ "bamarni/composer-bin-plugin": "^1.4.1",
185
+ "http-interop/http-factory-tests": "^0.9",
186
+ "phpunit/phpunit": "^8.5.8 || ^9.3.10"
187
+ },
188
+ "suggest": {
189
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
190
+ },
191
+ "type": "library",
192
+ "extra": {
193
+ "branch-alias": {
194
+ "dev-master": "2.3-dev"
195
+ }
196
+ },
197
+ "autoload": {
198
+ "psr-4": {
199
+ "GuzzleHttp\\Psr7\\": "src/"
200
+ }
201
+ },
202
+ "notification-url": "https://packagist.org/downloads/",
203
+ "license": [
204
+ "MIT"
205
+ ],
206
+ "authors": [
207
+ {
208
+ "name": "Graham Campbell",
209
+ "email": "hello@gjcampbell.co.uk",
210
+ "homepage": "https://github.com/GrahamCampbell"
211
+ },
212
+ {
213
+ "name": "Michael Dowling",
214
+ "email": "mtdowling@gmail.com",
215
+ "homepage": "https://github.com/mtdowling"
216
+ },
217
+ {
218
+ "name": "George Mponos",
219
+ "email": "gmponos@gmail.com",
220
+ "homepage": "https://github.com/gmponos"
221
+ },
222
+ {
223
+ "name": "Tobias Nyholm",
224
+ "email": "tobias.nyholm@gmail.com",
225
+ "homepage": "https://github.com/Nyholm"
226
+ },
227
+ {
228
+ "name": "Márk Sági-Kazár",
229
+ "email": "mark.sagikazar@gmail.com",
230
+ "homepage": "https://github.com/sagikazarmark"
231
+ },
232
+ {
233
+ "name": "Tobias Schultze",
234
+ "email": "webmaster@tubo-world.de",
235
+ "homepage": "https://github.com/Tobion"
236
+ },
237
+ {
238
+ "name": "Márk Sági-Kazár",
239
+ "email": "mark.sagikazar@gmail.com",
240
+ "homepage": "https://sagikazarmark.hu"
241
+ }
242
+ ],
243
+ "description": "PSR-7 message implementation that also provides common utility methods",
244
+ "keywords": [
245
+ "http",
246
+ "message",
247
+ "psr-7",
248
+ "request",
249
+ "response",
250
+ "stream",
251
+ "uri",
252
+ "url"
253
+ ],
254
+ "support": {
255
+ "issues": "https://github.com/guzzle/psr7/issues",
256
+ "source": "https://github.com/guzzle/psr7/tree/2.3.0"
257
+ },
258
+ "funding": [
259
+ {
260
+ "url": "https://github.com/GrahamCampbell",
261
+ "type": "github"
262
+ },
263
+ {
264
+ "url": "https://github.com/Nyholm",
265
+ "type": "github"
266
+ },
267
+ {
268
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
269
+ "type": "tidelift"
270
+ }
271
+ ],
272
+ "time": "2022-06-09T08:26:02+00:00"
273
+ },
274
+ {
275
+ "name": "http-interop/http-factory-guzzle",
276
+ "version": "1.2.0",
277
+ "source": {
278
+ "type": "git",
279
+ "url": "https://github.com/http-interop/http-factory-guzzle.git",
280
+ "reference": "8f06e92b95405216b237521cc64c804dd44c4a81"
281
+ },
282
+ "dist": {
283
+ "type": "zip",
284
+ "url": "https://api.github.com/repos/http-interop/http-factory-guzzle/zipball/8f06e92b95405216b237521cc64c804dd44c4a81",
285
+ "reference": "8f06e92b95405216b237521cc64c804dd44c4a81",
286
+ "shasum": ""
287
+ },
288
+ "require": {
289
+ "guzzlehttp/psr7": "^1.7||^2.0",
290
+ "php": ">=7.3",
291
+ "psr/http-factory": "^1.0"
292
+ },
293
+ "provide": {
294
+ "psr/http-factory-implementation": "^1.0"
295
+ },
296
+ "require-dev": {
297
+ "http-interop/http-factory-tests": "^0.9",
298
+ "phpunit/phpunit": "^9.5"
299
+ },
300
+ "suggest": {
301
+ "guzzlehttp/psr7": "Includes an HTTP factory starting in version 2.0"
302
+ },
303
+ "type": "library",
304
+ "autoload": {
305
+ "psr-4": {
306
+ "Http\\Factory\\Guzzle\\": "src/"
307
+ }
308
+ },
309
+ "notification-url": "https://packagist.org/downloads/",
310
+ "license": [
311
+ "MIT"
312
+ ],
313
+ "authors": [
314
+ {
315
+ "name": "PHP-FIG",
316
+ "homepage": "http://www.php-fig.org/"
317
+ }
318
+ ],
319
+ "description": "An HTTP Factory using Guzzle PSR7",
320
+ "keywords": [
321
+ "factory",
322
+ "http",
323
+ "psr-17",
324
+ "psr-7"
325
+ ],
326
+ "support": {
327
+ "issues": "https://github.com/http-interop/http-factory-guzzle/issues",
328
+ "source": "https://github.com/http-interop/http-factory-guzzle/tree/1.2.0"
329
+ },
330
+ "time": "2021-07-21T13:50:14+00:00"
331
+ },
332
+ {
333
+ "name": "jean85/pretty-package-versions",
334
+ "version": "2.0.5",
335
+ "source": {
336
+ "type": "git",
337
+ "url": "https://github.com/Jean85/pretty-package-versions.git",
338
+ "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af"
339
+ },
340
+ "dist": {
341
+ "type": "zip",
342
+ "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af",
343
+ "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af",
344
+ "shasum": ""
345
+ },
346
+ "require": {
347
+ "composer-runtime-api": "^2.0.0",
348
+ "php": "^7.1|^8.0"
349
+ },
350
+ "require-dev": {
351
+ "friendsofphp/php-cs-fixer": "^2.17",
352
+ "jean85/composer-provided-replaced-stub-package": "^1.0",
353
+ "phpstan/phpstan": "^0.12.66",
354
+ "phpunit/phpunit": "^7.5|^8.5|^9.4",
355
+ "vimeo/psalm": "^4.3"
356
+ },
357
+ "type": "library",
358
+ "extra": {
359
+ "branch-alias": {
360
+ "dev-master": "1.x-dev"
361
+ }
362
+ },
363
+ "autoload": {
364
+ "psr-4": {
365
+ "Jean85\\": "src/"
366
+ }
367
+ },
368
+ "notification-url": "https://packagist.org/downloads/",
369
+ "license": [
370
+ "MIT"
371
+ ],
372
+ "authors": [
373
+ {
374
+ "name": "Alessandro Lai",
375
+ "email": "alessandro.lai85@gmail.com"
376
+ }
377
+ ],
378
+ "description": "A library to get pretty versions strings of installed dependencies",
379
+ "keywords": [
380
+ "composer",
381
+ "package",
382
+ "release",
383
+ "versions"
384
+ ],
385
+ "support": {
386
+ "issues": "https://github.com/Jean85/pretty-package-versions/issues",
387
+ "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5"
388
+ },
389
+ "time": "2021-10-08T21:21:46+00:00"
390
+ },
391
+ {
392
+ "name": "php-http/client-common",
393
+ "version": "2.5.0",
394
+ "source": {
395
+ "type": "git",
396
+ "url": "https://github.com/php-http/client-common.git",
397
+ "reference": "d135751167d57e27c74de674d6a30cef2dc8e054"
398
+ },
399
+ "dist": {
400
+ "type": "zip",
401
+ "url": "https://api.github.com/repos/php-http/client-common/zipball/d135751167d57e27c74de674d6a30cef2dc8e054",
402
+ "reference": "d135751167d57e27c74de674d6a30cef2dc8e054",
403
+ "shasum": ""
404
+ },
405
+ "require": {
406
+ "php": "^7.1 || ^8.0",
407
+ "php-http/httplug": "^2.0",
408
+ "php-http/message": "^1.6",
409
+ "php-http/message-factory": "^1.0",
410
+ "psr/http-client": "^1.0",
411
+ "psr/http-factory": "^1.0",
412
+ "psr/http-message": "^1.0",
413
+ "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0",
414
+ "symfony/polyfill-php80": "^1.17"
415
+ },
416
+ "require-dev": {
417
+ "doctrine/instantiator": "^1.1",
418
+ "guzzlehttp/psr7": "^1.4",
419
+ "nyholm/psr7": "^1.2",
420
+ "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
421
+ "phpspec/prophecy": "^1.10.2",
422
+ "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3"
423
+ },
424
+ "suggest": {
425
+ "ext-json": "To detect JSON responses with the ContentTypePlugin",
426
+ "ext-libxml": "To detect XML responses with the ContentTypePlugin",
427
+ "php-http/cache-plugin": "PSR-6 Cache plugin",
428
+ "php-http/logger-plugin": "PSR-3 Logger plugin",
429
+ "php-http/stopwatch-plugin": "Symfony Stopwatch plugin"
430
+ },
431
+ "type": "library",
432
+ "extra": {
433
+ "branch-alias": {
434
+ "dev-master": "2.3.x-dev"
435
+ }
436
+ },
437
+ "autoload": {
438
+ "psr-4": {
439
+ "Http\\Client\\Common\\": "src/"
440
+ }
441
+ },
442
+ "notification-url": "https://packagist.org/downloads/",
443
+ "license": [
444
+ "MIT"
445
+ ],
446
+ "authors": [
447
+ {
448
+ "name": "Márk Sági-Kazár",
449
+ "email": "mark.sagikazar@gmail.com"
450
+ }
451
+ ],
452
+ "description": "Common HTTP Client implementations and tools for HTTPlug",
453
+ "homepage": "http://httplug.io",
454
+ "keywords": [
455
+ "client",
456
+ "common",
457
+ "http",
458
+ "httplug"
459
+ ],
460
+ "support": {
461
+ "issues": "https://github.com/php-http/client-common/issues",
462
+ "source": "https://github.com/php-http/client-common/tree/2.5.0"
463
+ },
464
+ "time": "2021-11-26T15:01:24+00:00"
465
+ },
466
+ {
467
+ "name": "php-http/discovery",
468
+ "version": "1.14.2",
469
+ "source": {
470
+ "type": "git",
471
+ "url": "https://github.com/php-http/discovery.git",
472
+ "reference": "c8d48852fbc052454af42f6de27635ddd916b959"
473
+ },
474
+ "dist": {
475
+ "type": "zip",
476
+ "url": "https://api.github.com/repos/php-http/discovery/zipball/c8d48852fbc052454af42f6de27635ddd916b959",
477
+ "reference": "c8d48852fbc052454af42f6de27635ddd916b959",
478
+ "shasum": ""
479
+ },
480
+ "require": {
481
+ "php": "^7.1 || ^8.0"
482
+ },
483
+ "conflict": {
484
+ "nyholm/psr7": "<1.0"
485
+ },
486
+ "require-dev": {
487
+ "graham-campbell/phpspec-skip-example-extension": "^5.0",
488
+ "php-http/httplug": "^1.0 || ^2.0",
489
+ "php-http/message-factory": "^1.0",
490
+ "phpspec/phpspec": "^5.1 || ^6.1"
491
+ },
492
+ "suggest": {
493
+ "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories"
494
+ },
495
+ "type": "library",
496
+ "extra": {
497
+ "branch-alias": {
498
+ "dev-master": "1.9-dev"
499
+ }
500
+ },
501
+ "autoload": {
502
+ "psr-4": {
503
+ "Http\\Discovery\\": "src/"
504
+ }
505
+ },
506
+ "notification-url": "https://packagist.org/downloads/",
507
+ "license": [
508
+ "MIT"
509
+ ],
510
+ "authors": [
511
+ {
512
+ "name": "Márk Sági-Kazár",
513
+ "email": "mark.sagikazar@gmail.com"
514
+ }
515
+ ],
516
+ "description": "Finds installed HTTPlug implementations and PSR-7 message factories",
517
+ "homepage": "http://php-http.org",
518
+ "keywords": [
519
+ "adapter",
520
+ "client",
521
+ "discovery",
522
+ "factory",
523
+ "http",
524
+ "message",
525
+ "psr7"
526
+ ],
527
+ "support": {
528
+ "issues": "https://github.com/php-http/discovery/issues",
529
+ "source": "https://github.com/php-http/discovery/tree/1.14.2"
530
+ },
531
+ "time": "2022-05-25T07:26:05+00:00"
532
+ },
533
+ {
534
+ "name": "php-http/httplug",
535
+ "version": "2.3.0",
536
+ "source": {
537
+ "type": "git",
538
+ "url": "https://github.com/php-http/httplug.git",
539
+ "reference": "f640739f80dfa1152533976e3c112477f69274eb"
540
+ },
541
+ "dist": {
542
+ "type": "zip",
543
+ "url": "https://api.github.com/repos/php-http/httplug/zipball/f640739f80dfa1152533976e3c112477f69274eb",
544
+ "reference": "f640739f80dfa1152533976e3c112477f69274eb",
545
+ "shasum": ""
546
+ },
547
+ "require": {
548
+ "php": "^7.1 || ^8.0",
549
+ "php-http/promise": "^1.1",
550
+ "psr/http-client": "^1.0",
551
+ "psr/http-message": "^1.0"
552
+ },
553
+ "require-dev": {
554
+ "friends-of-phpspec/phpspec-code-coverage": "^4.1",
555
+ "phpspec/phpspec": "^5.1 || ^6.0"
556
+ },
557
+ "type": "library",
558
+ "extra": {
559
+ "branch-alias": {
560
+ "dev-master": "2.x-dev"
561
+ }
562
+ },
563
+ "autoload": {
564
+ "psr-4": {
565
+ "Http\\Client\\": "src/"
566
+ }
567
+ },
568
+ "notification-url": "https://packagist.org/downloads/",
569
+ "license": [
570
+ "MIT"
571
+ ],
572
+ "authors": [
573
+ {
574
+ "name": "Eric GELOEN",
575
+ "email": "geloen.eric@gmail.com"
576
+ },
577
+ {
578
+ "name": "Márk Sági-Kazár",
579
+ "email": "mark.sagikazar@gmail.com",
580
+ "homepage": "https://sagikazarmark.hu"
581
+ }
582
+ ],
583
+ "description": "HTTPlug, the HTTP client abstraction for PHP",
584
+ "homepage": "http://httplug.io",
585
+ "keywords": [
586
+ "client",
587
+ "http"
588
+ ],
589
+ "support": {
590
+ "issues": "https://github.com/php-http/httplug/issues",
591
+ "source": "https://github.com/php-http/httplug/tree/2.3.0"
592
+ },
593
+ "time": "2022-02-21T09:52:22+00:00"
594
+ },
595
+ {
596
+ "name": "php-http/message",
597
+ "version": "1.13.0",
598
+ "source": {
599
+ "type": "git",
600
+ "url": "https://github.com/php-http/message.git",
601
+ "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361"
602
+ },
603
+ "dist": {
604
+ "type": "zip",
605
+ "url": "https://api.github.com/repos/php-http/message/zipball/7886e647a30a966a1a8d1dad1845b71ca8678361",
606
+ "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361",
607
+ "shasum": ""
608
+ },
609
+ "require": {
610
+ "clue/stream-filter": "^1.5",
611
+ "php": "^7.1 || ^8.0",
612
+ "php-http/message-factory": "^1.0.2",
613
+ "psr/http-message": "^1.0"
614
+ },
615
+ "provide": {
616
+ "php-http/message-factory-implementation": "1.0"
617
+ },
618
+ "require-dev": {
619
+ "ergebnis/composer-normalize": "^2.6",
620
+ "ext-zlib": "*",
621
+ "guzzlehttp/psr7": "^1.0",
622
+ "laminas/laminas-diactoros": "^2.0",
623
+ "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
624
+ "slim/slim": "^3.0"
625
+ },
626
+ "suggest": {
627
+ "ext-zlib": "Used with compressor/decompressor streams",
628
+ "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories",
629
+ "laminas/laminas-diactoros": "Used with Diactoros Factories",
630
+ "slim/slim": "Used with Slim Framework PSR-7 implementation"
631
+ },
632
+ "type": "library",
633
+ "extra": {
634
+ "branch-alias": {
635
+ "dev-master": "1.10-dev"
636
+ }
637
+ },
638
+ "autoload": {
639
+ "files": [
640
+ "src/filters.php"
641
+ ],
642
+ "psr-4": {
643
+ "Http\\Message\\": "src/"
644
+ }
645
+ },
646
+ "notification-url": "https://packagist.org/downloads/",
647
+ "license": [
648
+ "MIT"
649
+ ],
650
+ "authors": [
651
+ {
652
+ "name": "Márk Sági-Kazár",
653
+ "email": "mark.sagikazar@gmail.com"
654
+ }
655
+ ],
656
+ "description": "HTTP Message related tools",
657
+ "homepage": "http://php-http.org",
658
+ "keywords": [
659
+ "http",
660
+ "message",
661
+ "psr-7"
662
+ ],
663
+ "support": {
664
+ "issues": "https://github.com/php-http/message/issues",
665
+ "source": "https://github.com/php-http/message/tree/1.13.0"
666
+ },
667
+ "time": "2022-02-11T13:41:14+00:00"
668
+ },
669
+ {
670
+ "name": "php-http/message-factory",
671
+ "version": "v1.0.2",
672
+ "source": {
673
+ "type": "git",
674
+ "url": "https://github.com/php-http/message-factory.git",
675
+ "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1"
676
+ },
677
+ "dist": {
678
+ "type": "zip",
679
+ "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1",
680
+ "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1",
681
+ "shasum": ""
682
+ },
683
+ "require": {
684
+ "php": ">=5.4",
685
+ "psr/http-message": "^1.0"
686
+ },
687
+ "type": "library",
688
+ "extra": {
689
+ "branch-alias": {
690
+ "dev-master": "1.0-dev"
691
+ }
692
+ },
693
+ "autoload": {
694
+ "psr-4": {
695
+ "Http\\Message\\": "src/"
696
+ }
697
+ },
698
+ "notification-url": "https://packagist.org/downloads/",
699
+ "license": [
700
+ "MIT"
701
+ ],
702
+ "authors": [
703
+ {
704
+ "name": "Márk Sági-Kazár",
705
+ "email": "mark.sagikazar@gmail.com"
706
+ }
707
+ ],
708
+ "description": "Factory interfaces for PSR-7 HTTP Message",
709
+ "homepage": "http://php-http.org",
710
+ "keywords": [
711
+ "factory",
712
+ "http",
713
+ "message",
714
+ "stream",
715
+ "uri"
716
+ ],
717
+ "support": {
718
+ "issues": "https://github.com/php-http/message-factory/issues",
719
+ "source": "https://github.com/php-http/message-factory/tree/master"
720
+ },
721
+ "time": "2015-12-19T14:08:53+00:00"
722
+ },
723
+ {
724
+ "name": "php-http/promise",
725
+ "version": "1.1.0",
726
+ "source": {
727
+ "type": "git",
728
+ "url": "https://github.com/php-http/promise.git",
729
+ "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88"
730
+ },
731
+ "dist": {
732
+ "type": "zip",
733
+ "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
734
+ "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
735
+ "shasum": ""
736
+ },
737
+ "require": {
738
+ "php": "^7.1 || ^8.0"
739
+ },
740
+ "require-dev": {
741
+ "friends-of-phpspec/phpspec-code-coverage": "^4.3.2",
742
+ "phpspec/phpspec": "^5.1.2 || ^6.2"
743
+ },
744
+ "type": "library",
745
+ "extra": {
746
+ "branch-alias": {
747
+ "dev-master": "1.1-dev"
748
+ }
749
+ },
750
+ "autoload": {
751
+ "psr-4": {
752
+ "Http\\Promise\\": "src/"
753
+ }
754
+ },
755
+ "notification-url": "https://packagist.org/downloads/",
756
+ "license": [
757
+ "MIT"
758
+ ],
759
+ "authors": [
760
+ {
761
+ "name": "Joel Wurtz",
762
+ "email": "joel.wurtz@gmail.com"
763
+ },
764
+ {
765
+ "name": "Márk Sági-Kazár",
766
+ "email": "mark.sagikazar@gmail.com"
767
+ }
768
+ ],
769
+ "description": "Promise used for asynchronous HTTP requests",
770
+ "homepage": "http://httplug.io",
771
+ "keywords": [
772
+ "promise"
773
+ ],
774
+ "support": {
775
+ "issues": "https://github.com/php-http/promise/issues",
776
+ "source": "https://github.com/php-http/promise/tree/1.1.0"
777
+ },
778
+ "time": "2020-07-07T09:29:14+00:00"
779
+ },
780
+ {
781
+ "name": "psr/container",
782
+ "version": "1.1.2",
783
+ "source": {
784
+ "type": "git",
785
+ "url": "https://github.com/php-fig/container.git",
786
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
787
+ },
788
+ "dist": {
789
+ "type": "zip",
790
+ "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
791
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
792
+ "shasum": ""
793
+ },
794
+ "require": {
795
+ "php": ">=7.4.0"
796
+ },
797
+ "type": "library",
798
+ "autoload": {
799
+ "psr-4": {
800
+ "Psr\\Container\\": "src/"
801
+ }
802
+ },
803
+ "notification-url": "https://packagist.org/downloads/",
804
+ "license": [
805
+ "MIT"
806
+ ],
807
+ "authors": [
808
+ {
809
+ "name": "PHP-FIG",
810
+ "homepage": "https://www.php-fig.org/"
811
+ }
812
+ ],
813
+ "description": "Common Container Interface (PHP FIG PSR-11)",
814
+ "homepage": "https://github.com/php-fig/container",
815
+ "keywords": [
816
+ "PSR-11",
817
+ "container",
818
+ "container-interface",
819
+ "container-interop",
820
+ "psr"
821
+ ],
822
+ "support": {
823
+ "issues": "https://github.com/php-fig/container/issues",
824
+ "source": "https://github.com/php-fig/container/tree/1.1.2"
825
+ },
826
+ "time": "2021-11-05T16:50:12+00:00"
827
+ },
828
+ {
829
+ "name": "psr/http-client",
830
+ "version": "1.0.1",
831
+ "source": {
832
+ "type": "git",
833
+ "url": "https://github.com/php-fig/http-client.git",
834
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
835
+ },
836
+ "dist": {
837
+ "type": "zip",
838
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
839
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
840
+ "shasum": ""
841
+ },
842
+ "require": {
843
+ "php": "^7.0 || ^8.0",
844
+ "psr/http-message": "^1.0"
845
+ },
846
+ "type": "library",
847
+ "extra": {
848
+ "branch-alias": {
849
+ "dev-master": "1.0.x-dev"
850
+ }
851
+ },
852
+ "autoload": {
853
+ "psr-4": {
854
+ "Psr\\Http\\Client\\": "src/"
855
+ }
856
+ },
857
+ "notification-url": "https://packagist.org/downloads/",
858
+ "license": [
859
+ "MIT"
860
+ ],
861
+ "authors": [
862
+ {
863
+ "name": "PHP-FIG",
864
+ "homepage": "http://www.php-fig.org/"
865
+ }
866
+ ],
867
+ "description": "Common interface for HTTP clients",
868
+ "homepage": "https://github.com/php-fig/http-client",
869
+ "keywords": [
870
+ "http",
871
+ "http-client",
872
+ "psr",
873
+ "psr-18"
874
+ ],
875
+ "support": {
876
+ "source": "https://github.com/php-fig/http-client/tree/master"
877
+ },
878
+ "time": "2020-06-29T06:28:15+00:00"
879
+ },
880
+ {
881
+ "name": "psr/http-factory",
882
+ "version": "1.0.1",
883
+ "source": {
884
+ "type": "git",
885
+ "url": "https://github.com/php-fig/http-factory.git",
886
+ "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
887
+ },
888
+ "dist": {
889
+ "type": "zip",
890
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
891
+ "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
892
+ "shasum": ""
893
+ },
894
+ "require": {
895
+ "php": ">=7.0.0",
896
+ "psr/http-message": "^1.0"
897
+ },
898
+ "type": "library",
899
+ "extra": {
900
+ "branch-alias": {
901
+ "dev-master": "1.0.x-dev"
902
+ }
903
+ },
904
+ "autoload": {
905
+ "psr-4": {
906
+ "Psr\\Http\\Message\\": "src/"
907
+ }
908
+ },
909
+ "notification-url": "https://packagist.org/downloads/",
910
+ "license": [
911
+ "MIT"
912
+ ],
913
+ "authors": [
914
+ {
915
+ "name": "PHP-FIG",
916
+ "homepage": "http://www.php-fig.org/"
917
+ }
918
+ ],
919
+ "description": "Common interfaces for PSR-7 HTTP message factories",
920
+ "keywords": [
921
+ "factory",
922
+ "http",
923
+ "message",
924
+ "psr",
925
+ "psr-17",
926
+ "psr-7",
927
+ "request",
928
+ "response"
929
+ ],
930
+ "support": {
931
+ "source": "https://github.com/php-fig/http-factory/tree/master"
932
+ },
933
+ "time": "2019-04-30T12:38:16+00:00"
934
+ },
935
+ {
936
+ "name": "psr/http-message",
937
+ "version": "1.0.1",
938
+ "source": {
939
+ "type": "git",
940
+ "url": "https://github.com/php-fig/http-message.git",
941
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
942
+ },
943
+ "dist": {
944
+ "type": "zip",
945
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
946
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
947
+ "shasum": ""
948
+ },
949
+ "require": {
950
+ "php": ">=5.3.0"
951
+ },
952
+ "type": "library",
953
+ "extra": {
954
+ "branch-alias": {
955
+ "dev-master": "1.0.x-dev"
956
+ }
957
+ },
958
+ "autoload": {
959
+ "psr-4": {
960
+ "Psr\\Http\\Message\\": "src/"
961
+ }
962
+ },
963
+ "notification-url": "https://packagist.org/downloads/",
964
+ "license": [
965
+ "MIT"
966
+ ],
967
+ "authors": [
968
+ {
969
+ "name": "PHP-FIG",
970
+ "homepage": "http://www.php-fig.org/"
971
+ }
972
+ ],
973
+ "description": "Common interface for HTTP messages",
974
+ "homepage": "https://github.com/php-fig/http-message",
975
+ "keywords": [
976
+ "http",
977
+ "http-message",
978
+ "psr",
979
+ "psr-7",
980
+ "request",
981
+ "response"
982
+ ],
983
+ "support": {
984
+ "source": "https://github.com/php-fig/http-message/tree/master"
985
+ },
986
+ "time": "2016-08-06T14:39:51+00:00"
987
+ },
988
+ {
989
+ "name": "psr/log",
990
+ "version": "1.1.4",
991
+ "source": {
992
+ "type": "git",
993
+ "url": "https://github.com/php-fig/log.git",
994
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
995
+ },
996
+ "dist": {
997
+ "type": "zip",
998
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
999
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
1000
+ "shasum": ""
1001
+ },
1002
+ "require": {
1003
+ "php": ">=5.3.0"
1004
+ },
1005
+ "type": "library",
1006
+ "extra": {
1007
+ "branch-alias": {
1008
+ "dev-master": "1.1.x-dev"
1009
+ }
1010
+ },
1011
+ "autoload": {
1012
+ "psr-4": {
1013
+ "Psr\\Log\\": "Psr/Log/"
1014
+ }
1015
+ },
1016
+ "notification-url": "https://packagist.org/downloads/",
1017
+ "license": [
1018
+ "MIT"
1019
+ ],
1020
+ "authors": [
1021
+ {
1022
+ "name": "PHP-FIG",
1023
+ "homepage": "https://www.php-fig.org/"
1024
+ }
1025
+ ],
1026
+ "description": "Common interface for logging libraries",
1027
+ "homepage": "https://github.com/php-fig/log",
1028
+ "keywords": [
1029
+ "log",
1030
+ "psr",
1031
+ "psr-3"
1032
+ ],
1033
+ "support": {
1034
+ "source": "https://github.com/php-fig/log/tree/1.1.4"
1035
+ },
1036
+ "time": "2021-05-03T11:20:27+00:00"
1037
+ },
1038
+ {
1039
+ "name": "ralouphie/getallheaders",
1040
+ "version": "3.0.3",
1041
+ "source": {
1042
+ "type": "git",
1043
+ "url": "https://github.com/ralouphie/getallheaders.git",
1044
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
1045
+ },
1046
+ "dist": {
1047
+ "type": "zip",
1048
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
1049
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
1050
+ "shasum": ""
1051
+ },
1052
+ "require": {
1053
+ "php": ">=5.6"
1054
+ },
1055
+ "require-dev": {
1056
+ "php-coveralls/php-coveralls": "^2.1",
1057
+ "phpunit/phpunit": "^5 || ^6.5"
1058
+ },
1059
+ "type": "library",
1060
+ "autoload": {
1061
+ "files": [
1062
+ "src/getallheaders.php"
1063
+ ]
1064
+ },
1065
+ "notification-url": "https://packagist.org/downloads/",
1066
+ "license": [
1067
+ "MIT"
1068
+ ],
1069
+ "authors": [
1070
+ {
1071
+ "name": "Ralph Khattar",
1072
+ "email": "ralph.khattar@gmail.com"
1073
+ }
1074
+ ],
1075
+ "description": "A polyfill for getallheaders.",
1076
+ "support": {
1077
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
1078
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
1079
+ },
1080
+ "time": "2019-03-08T08:55:37+00:00"
1081
+ },
1082
+ {
1083
+ "name": "sentry/sdk",
1084
+ "version": "3.2.0",
1085
+ "source": {
1086
+ "type": "git",
1087
+ "url": "https://github.com/getsentry/sentry-php-sdk.git",
1088
+ "reference": "6d78bd83b43efbb52f81d6824f4af344fa9ba292"
1089
+ },
1090
+ "dist": {
1091
+ "type": "zip",
1092
+ "url": "https://api.github.com/repos/getsentry/sentry-php-sdk/zipball/6d78bd83b43efbb52f81d6824f4af344fa9ba292",
1093
+ "reference": "6d78bd83b43efbb52f81d6824f4af344fa9ba292",
1094
+ "shasum": ""
1095
+ },
1096
+ "require": {
1097
+ "http-interop/http-factory-guzzle": "^1.0",
1098
+ "sentry/sentry": "^3.5",
1099
+ "symfony/http-client": "^4.3|^5.0|^6.0"
1100
+ },
1101
+ "type": "metapackage",
1102
+ "notification-url": "https://packagist.org/downloads/",
1103
+ "license": [
1104
+ "MIT"
1105
+ ],
1106
+ "authors": [
1107
+ {
1108
+ "name": "Sentry",
1109
+ "email": "accounts@sentry.io"
1110
+ }
1111
+ ],
1112
+ "description": "This is a metapackage shipping sentry/sentry with a recommended HTTP client.",
1113
+ "homepage": "http://sentry.io",
1114
+ "keywords": [
1115
+ "crash-reporting",
1116
+ "crash-reports",
1117
+ "error-handler",
1118
+ "error-monitoring",
1119
+ "log",
1120
+ "logging",
1121
+ "sentry"
1122
+ ],
1123
+ "support": {
1124
+ "issues": "https://github.com/getsentry/sentry-php-sdk/issues",
1125
+ "source": "https://github.com/getsentry/sentry-php-sdk/tree/3.2.0"
1126
+ },
1127
+ "funding": [
1128
+ {
1129
+ "url": "https://sentry.io/",
1130
+ "type": "custom"
1131
+ },
1132
+ {
1133
+ "url": "https://sentry.io/pricing/",
1134
+ "type": "custom"
1135
+ }
1136
+ ],
1137
+ "time": "2022-05-21T11:10:11+00:00"
1138
+ },
1139
+ {
1140
+ "name": "sentry/sentry",
1141
+ "version": "3.6.0",
1142
+ "source": {
1143
+ "type": "git",
1144
+ "url": "https://github.com/getsentry/sentry-php.git",
1145
+ "reference": "6d1a6ee29c558be373bfe08d454a3c116c02dd0d"
1146
+ },
1147
+ "dist": {
1148
+ "type": "zip",
1149
+ "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/6d1a6ee29c558be373bfe08d454a3c116c02dd0d",
1150
+ "reference": "6d1a6ee29c558be373bfe08d454a3c116c02dd0d",
1151
+ "shasum": ""
1152
+ },
1153
+ "require": {
1154
+ "ext-json": "*",
1155
+ "ext-mbstring": "*",
1156
+ "guzzlehttp/promises": "^1.4",
1157
+ "guzzlehttp/psr7": "^1.8.4|^2.1.1",
1158
+ "jean85/pretty-package-versions": "^1.5|^2.0.4",
1159
+ "php": "^7.2|^8.0",
1160
+ "php-http/async-client-implementation": "^1.0",
1161
+ "php-http/client-common": "^1.5|^2.0",
1162
+ "php-http/discovery": "^1.11",
1163
+ "php-http/httplug": "^1.1|^2.0",
1164
+ "php-http/message": "^1.5",
1165
+ "psr/http-factory": "^1.0",
1166
+ "psr/http-message-implementation": "^1.0",
1167
+ "psr/log": "^1.0|^2.0|^3.0",
1168
+ "symfony/options-resolver": "^3.4.43|^4.4.30|^5.0.11|^6.0",
1169
+ "symfony/polyfill-php80": "^1.17",
1170
+ "symfony/polyfill-uuid": "^1.13.1"
1171
+ },
1172
+ "conflict": {
1173
+ "php-http/client-common": "1.8.0",
1174
+ "raven/raven": "*"
1175
+ },
1176
+ "require-dev": {
1177
+ "friendsofphp/php-cs-fixer": "^2.19|3.4.*",
1178
+ "http-interop/http-factory-guzzle": "^1.0",
1179
+ "monolog/monolog": "^1.6|^2.0|^3.0",
1180
+ "nikic/php-parser": "^4.10.3",
1181
+ "php-http/mock-client": "^1.3",
1182
+ "phpbench/phpbench": "^1.0",
1183
+ "phpstan/extension-installer": "^1.0",
1184
+ "phpstan/phpstan": "^1.3",
1185
+ "phpstan/phpstan-phpunit": "^1.0",
1186
+ "phpunit/phpunit": "^8.5.14|^9.4",
1187
+ "symfony/phpunit-bridge": "^5.2|^6.0",
1188
+ "vimeo/psalm": "^4.17"
1189
+ },
1190
+ "suggest": {
1191
+ "monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler."
1192
+ },
1193
+ "type": "library",
1194
+ "extra": {
1195
+ "branch-alias": {
1196
+ "dev-master": "3.6.x-dev"
1197
+ }
1198
+ },
1199
+ "autoload": {
1200
+ "files": [
1201
+ "src/functions.php"
1202
+ ],
1203
+ "psr-4": {
1204
+ "Sentry\\": "src/"
1205
+ }
1206
+ },
1207
+ "notification-url": "https://packagist.org/downloads/",
1208
+ "license": [
1209
+ "BSD-3-Clause"
1210
+ ],
1211
+ "authors": [
1212
+ {
1213
+ "name": "Sentry",
1214
+ "email": "accounts@sentry.io"
1215
+ }
1216
+ ],
1217
+ "description": "A PHP SDK for Sentry (http://sentry.io)",
1218
+ "homepage": "http://sentry.io",
1219
+ "keywords": [
1220
+ "crash-reporting",
1221
+ "crash-reports",
1222
+ "error-handler",
1223
+ "error-monitoring",
1224
+ "log",
1225
+ "logging",
1226
+ "sentry"
1227
+ ],
1228
+ "support": {
1229
+ "issues": "https://github.com/getsentry/sentry-php/issues",
1230
+ "source": "https://github.com/getsentry/sentry-php/tree/3.6.0"
1231
+ },
1232
+ "funding": [
1233
+ {
1234
+ "url": "https://sentry.io/",
1235
+ "type": "custom"
1236
+ },
1237
+ {
1238
+ "url": "https://sentry.io/pricing/",
1239
+ "type": "custom"
1240
+ }
1241
+ ],
1242
+ "time": "2022-06-09T20:33:39+00:00"
1243
+ },
1244
+ {
1245
+ "name": "symfony/deprecation-contracts",
1246
+ "version": "v2.5.1",
1247
+ "source": {
1248
+ "type": "git",
1249
+ "url": "https://github.com/symfony/deprecation-contracts.git",
1250
+ "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
1251
+ },
1252
+ "dist": {
1253
+ "type": "zip",
1254
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
1255
+ "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
1256
+ "shasum": ""
1257
+ },
1258
+ "require": {
1259
+ "php": ">=7.1"
1260
+ },
1261
+ "type": "library",
1262
+ "extra": {
1263
+ "branch-alias": {
1264
+ "dev-main": "2.5-dev"
1265
+ },
1266
+ "thanks": {
1267
+ "name": "symfony/contracts",
1268
+ "url": "https://github.com/symfony/contracts"
1269
+ }
1270
+ },
1271
+ "autoload": {
1272
+ "files": [
1273
+ "function.php"
1274
+ ]
1275
+ },
1276
+ "notification-url": "https://packagist.org/downloads/",
1277
+ "license": [
1278
+ "MIT"
1279
+ ],
1280
+ "authors": [
1281
+ {
1282
+ "name": "Nicolas Grekas",
1283
+ "email": "p@tchwork.com"
1284
+ },
1285
+ {
1286
+ "name": "Symfony Community",
1287
+ "homepage": "https://symfony.com/contributors"
1288
+ }
1289
+ ],
1290
+ "description": "A generic function and convention to trigger deprecation notices",
1291
+ "homepage": "https://symfony.com",
1292
+ "support": {
1293
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1"
1294
+ },
1295
+ "funding": [
1296
+ {
1297
+ "url": "https://symfony.com/sponsor",
1298
+ "type": "custom"
1299
+ },
1300
+ {
1301
+ "url": "https://github.com/fabpot",
1302
+ "type": "github"
1303
+ },
1304
+ {
1305
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1306
+ "type": "tidelift"
1307
+ }
1308
+ ],
1309
+ "time": "2022-01-02T09:53:40+00:00"
1310
+ },
1311
+ {
1312
+ "name": "symfony/http-client",
1313
+ "version": "v5.4.9",
1314
+ "source": {
1315
+ "type": "git",
1316
+ "url": "https://github.com/symfony/http-client.git",
1317
+ "reference": "dc0b15e42b762c040761c1eb9ce86a55d47cf672"
1318
+ },
1319
+ "dist": {
1320
+ "type": "zip",
1321
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/dc0b15e42b762c040761c1eb9ce86a55d47cf672",
1322
+ "reference": "dc0b15e42b762c040761c1eb9ce86a55d47cf672",
1323
+ "shasum": ""
1324
+ },
1325
+ "require": {
1326
+ "php": ">=7.2.5",
1327
+ "psr/log": "^1|^2|^3",
1328
+ "symfony/deprecation-contracts": "^2.1|^3",
1329
+ "symfony/http-client-contracts": "^2.4",
1330
+ "symfony/polyfill-php73": "^1.11",
1331
+ "symfony/polyfill-php80": "^1.16",
1332
+ "symfony/service-contracts": "^1.0|^2|^3"
1333
+ },
1334
+ "provide": {
1335
+ "php-http/async-client-implementation": "*",
1336
+ "php-http/client-implementation": "*",
1337
+ "psr/http-client-implementation": "1.0",
1338
+ "symfony/http-client-implementation": "2.4"
1339
+ },
1340
+ "require-dev": {
1341
+ "amphp/amp": "^2.5",
1342
+ "amphp/http-client": "^4.2.1",
1343
+ "amphp/http-tunnel": "^1.0",
1344
+ "amphp/socket": "^1.1",
1345
+ "guzzlehttp/promises": "^1.4",
1346
+ "nyholm/psr7": "^1.0",
1347
+ "php-http/httplug": "^1.0|^2.0",
1348
+ "psr/http-client": "^1.0",
1349
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
1350
+ "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0",
1351
+ "symfony/process": "^4.4|^5.0|^6.0",
1352
+ "symfony/stopwatch": "^4.4|^5.0|^6.0"
1353
+ },
1354
+ "type": "library",
1355
+ "autoload": {
1356
+ "psr-4": {
1357
+ "Symfony\\Component\\HttpClient\\": ""
1358
+ },
1359
+ "exclude-from-classmap": [
1360
+ "/Tests/"
1361
+ ]
1362
+ },
1363
+ "notification-url": "https://packagist.org/downloads/",
1364
+ "license": [
1365
+ "MIT"
1366
+ ],
1367
+ "authors": [
1368
+ {
1369
+ "name": "Nicolas Grekas",
1370
+ "email": "p@tchwork.com"
1371
+ },
1372
+ {
1373
+ "name": "Symfony Community",
1374
+ "homepage": "https://symfony.com/contributors"
1375
+ }
1376
+ ],
1377
+ "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
1378
+ "homepage": "https://symfony.com",
1379
+ "support": {
1380
+ "source": "https://github.com/symfony/http-client/tree/v5.4.9"
1381
+ },
1382
+ "funding": [
1383
+ {
1384
+ "url": "https://symfony.com/sponsor",
1385
+ "type": "custom"
1386
+ },
1387
+ {
1388
+ "url": "https://github.com/fabpot",
1389
+ "type": "github"
1390
+ },
1391
+ {
1392
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1393
+ "type": "tidelift"
1394
+ }
1395
+ ],
1396
+ "time": "2022-05-21T08:57:05+00:00"
1397
+ },
1398
+ {
1399
+ "name": "symfony/http-client-contracts",
1400
+ "version": "v2.5.1",
1401
+ "source": {
1402
+ "type": "git",
1403
+ "url": "https://github.com/symfony/http-client-contracts.git",
1404
+ "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5"
1405
+ },
1406
+ "dist": {
1407
+ "type": "zip",
1408
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1a4f708e4e87f335d1b1be6148060739152f0bd5",
1409
+ "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5",
1410
+ "shasum": ""
1411
+ },
1412
+ "require": {
1413
+ "php": ">=7.2.5"
1414
+ },
1415
+ "suggest": {
1416
+ "symfony/http-client-implementation": ""
1417
+ },
1418
+ "type": "library",
1419
+ "extra": {
1420
+ "branch-alias": {
1421
+ "dev-main": "2.5-dev"
1422
+ },
1423
+ "thanks": {
1424
+ "name": "symfony/contracts",
1425
+ "url": "https://github.com/symfony/contracts"
1426
+ }
1427
+ },
1428
+ "autoload": {
1429
+ "psr-4": {
1430
+ "Symfony\\Contracts\\HttpClient\\": ""
1431
+ }
1432
+ },
1433
+ "notification-url": "https://packagist.org/downloads/",
1434
+ "license": [
1435
+ "MIT"
1436
+ ],
1437
+ "authors": [
1438
+ {
1439
+ "name": "Nicolas Grekas",
1440
+ "email": "p@tchwork.com"
1441
+ },
1442
+ {
1443
+ "name": "Symfony Community",
1444
+ "homepage": "https://symfony.com/contributors"
1445
+ }
1446
+ ],
1447
+ "description": "Generic abstractions related to HTTP clients",
1448
+ "homepage": "https://symfony.com",
1449
+ "keywords": [
1450
+ "abstractions",
1451
+ "contracts",
1452
+ "decoupling",
1453
+ "interfaces",
1454
+ "interoperability",
1455
+ "standards"
1456
+ ],
1457
+ "support": {
1458
+ "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.1"
1459
+ },
1460
+ "funding": [
1461
+ {
1462
+ "url": "https://symfony.com/sponsor",
1463
+ "type": "custom"
1464
+ },
1465
+ {
1466
+ "url": "https://github.com/fabpot",
1467
+ "type": "github"
1468
+ },
1469
+ {
1470
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1471
+ "type": "tidelift"
1472
+ }
1473
+ ],
1474
+ "time": "2022-03-13T20:07:29+00:00"
1475
+ },
1476
+ {
1477
+ "name": "symfony/options-resolver",
1478
+ "version": "v5.4.3",
1479
+ "source": {
1480
+ "type": "git",
1481
+ "url": "https://github.com/symfony/options-resolver.git",
1482
+ "reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8"
1483
+ },
1484
+ "dist": {
1485
+ "type": "zip",
1486
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/cc1147cb11af1b43f503ac18f31aa3bec213aba8",
1487
+ "reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8",
1488
+ "shasum": ""
1489
+ },
1490
+ "require": {
1491
+ "php": ">=7.2.5",
1492
+ "symfony/deprecation-contracts": "^2.1|^3",
1493
+ "symfony/polyfill-php73": "~1.0",
1494
+ "symfony/polyfill-php80": "^1.16"
1495
+ },
1496
+ "type": "library",
1497
+ "autoload": {
1498
+ "psr-4": {
1499
+ "Symfony\\Component\\OptionsResolver\\": ""
1500
+ },
1501
+ "exclude-from-classmap": [
1502
+ "/Tests/"
1503
+ ]
1504
+ },
1505
+ "notification-url": "https://packagist.org/downloads/",
1506
+ "license": [
1507
+ "MIT"
1508
+ ],
1509
+ "authors": [
1510
+ {
1511
+ "name": "Fabien Potencier",
1512
+ "email": "fabien@symfony.com"
1513
+ },
1514
+ {
1515
+ "name": "Symfony Community",
1516
+ "homepage": "https://symfony.com/contributors"
1517
+ }
1518
+ ],
1519
+ "description": "Provides an improved replacement for the array_replace PHP function",
1520
+ "homepage": "https://symfony.com",
1521
+ "keywords": [
1522
+ "config",
1523
+ "configuration",
1524
+ "options"
1525
+ ],
1526
+ "support": {
1527
+ "source": "https://github.com/symfony/options-resolver/tree/v5.4.3"
1528
+ },
1529
+ "funding": [
1530
+ {
1531
+ "url": "https://symfony.com/sponsor",
1532
+ "type": "custom"
1533
+ },
1534
+ {
1535
+ "url": "https://github.com/fabpot",
1536
+ "type": "github"
1537
+ },
1538
+ {
1539
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1540
+ "type": "tidelift"
1541
+ }
1542
+ ],
1543
+ "time": "2022-01-02T09:53:40+00:00"
1544
+ },
1545
+ {
1546
+ "name": "symfony/polyfill-php73",
1547
+ "version": "v1.26.0",
1548
+ "source": {
1549
+ "type": "git",
1550
+ "url": "https://github.com/symfony/polyfill-php73.git",
1551
+ "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
1552
+ },
1553
+ "dist": {
1554
+ "type": "zip",
1555
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
1556
+ "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
1557
+ "shasum": ""
1558
+ },
1559
+ "require": {
1560
+ "php": ">=7.1"
1561
+ },
1562
+ "type": "library",
1563
+ "extra": {
1564
+ "branch-alias": {
1565
+ "dev-main": "1.26-dev"
1566
+ },
1567
+ "thanks": {
1568
+ "name": "symfony/polyfill",
1569
+ "url": "https://github.com/symfony/polyfill"
1570
+ }
1571
+ },
1572
+ "autoload": {
1573
+ "files": [
1574
+ "bootstrap.php"
1575
+ ],
1576
+ "psr-4": {
1577
+ "Symfony\\Polyfill\\Php73\\": ""
1578
+ },
1579
+ "classmap": [
1580
+ "Resources/stubs"
1581
+ ]
1582
+ },
1583
+ "notification-url": "https://packagist.org/downloads/",
1584
+ "license": [
1585
+ "MIT"
1586
+ ],
1587
+ "authors": [
1588
+ {
1589
+ "name": "Nicolas Grekas",
1590
+ "email": "p@tchwork.com"
1591
+ },
1592
+ {
1593
+ "name": "Symfony Community",
1594
+ "homepage": "https://symfony.com/contributors"
1595
+ }
1596
+ ],
1597
+ "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
1598
+ "homepage": "https://symfony.com",
1599
+ "keywords": [
1600
+ "compatibility",
1601
+ "polyfill",
1602
+ "portable",
1603
+ "shim"
1604
+ ],
1605
+ "support": {
1606
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
1607
+ },
1608
+ "funding": [
1609
+ {
1610
+ "url": "https://symfony.com/sponsor",
1611
+ "type": "custom"
1612
+ },
1613
+ {
1614
+ "url": "https://github.com/fabpot",
1615
+ "type": "github"
1616
+ },
1617
+ {
1618
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1619
+ "type": "tidelift"
1620
+ }
1621
+ ],
1622
+ "time": "2022-05-24T11:49:31+00:00"
1623
+ },
1624
+ {
1625
+ "name": "symfony/polyfill-php80",
1626
+ "version": "v1.26.0",
1627
+ "source": {
1628
+ "type": "git",
1629
+ "url": "https://github.com/symfony/polyfill-php80.git",
1630
+ "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
1631
+ },
1632
+ "dist": {
1633
+ "type": "zip",
1634
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
1635
+ "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
1636
+ "shasum": ""
1637
+ },
1638
+ "require": {
1639
+ "php": ">=7.1"
1640
+ },
1641
+ "type": "library",
1642
+ "extra": {
1643
+ "branch-alias": {
1644
+ "dev-main": "1.26-dev"
1645
+ },
1646
+ "thanks": {
1647
+ "name": "symfony/polyfill",
1648
+ "url": "https://github.com/symfony/polyfill"
1649
+ }
1650
+ },
1651
+ "autoload": {
1652
+ "files": [
1653
+ "bootstrap.php"
1654
+ ],
1655
+ "psr-4": {
1656
+ "Symfony\\Polyfill\\Php80\\": ""
1657
+ },
1658
+ "classmap": [
1659
+ "Resources/stubs"
1660
+ ]
1661
+ },
1662
+ "notification-url": "https://packagist.org/downloads/",
1663
+ "license": [
1664
+ "MIT"
1665
+ ],
1666
+ "authors": [
1667
+ {
1668
+ "name": "Ion Bazan",
1669
+ "email": "ion.bazan@gmail.com"
1670
+ },
1671
+ {
1672
+ "name": "Nicolas Grekas",
1673
+ "email": "p@tchwork.com"
1674
+ },
1675
+ {
1676
+ "name": "Symfony Community",
1677
+ "homepage": "https://symfony.com/contributors"
1678
+ }
1679
+ ],
1680
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
1681
+ "homepage": "https://symfony.com",
1682
+ "keywords": [
1683
+ "compatibility",
1684
+ "polyfill",
1685
+ "portable",
1686
+ "shim"
1687
+ ],
1688
+ "support": {
1689
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
1690
+ },
1691
+ "funding": [
1692
+ {
1693
+ "url": "https://symfony.com/sponsor",
1694
+ "type": "custom"
1695
+ },
1696
+ {
1697
+ "url": "https://github.com/fabpot",
1698
+ "type": "github"
1699
+ },
1700
+ {
1701
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1702
+ "type": "tidelift"
1703
+ }
1704
+ ],
1705
+ "time": "2022-05-10T07:21:04+00:00"
1706
+ },
1707
+ {
1708
+ "name": "symfony/polyfill-uuid",
1709
+ "version": "v1.26.0",
1710
+ "source": {
1711
+ "type": "git",
1712
+ "url": "https://github.com/symfony/polyfill-uuid.git",
1713
+ "reference": "a41886c1c81dc075a09c71fe6db5b9d68c79de23"
1714
+ },
1715
+ "dist": {
1716
+ "type": "zip",
1717
+ "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/a41886c1c81dc075a09c71fe6db5b9d68c79de23",
1718
+ "reference": "a41886c1c81dc075a09c71fe6db5b9d68c79de23",
1719
+ "shasum": ""
1720
+ },
1721
+ "require": {
1722
+ "php": ">=7.1"
1723
+ },
1724
+ "provide": {
1725
+ "ext-uuid": "*"
1726
+ },
1727
+ "suggest": {
1728
+ "ext-uuid": "For best performance"
1729
+ },
1730
+ "type": "library",
1731
+ "extra": {
1732
+ "branch-alias": {
1733
+ "dev-main": "1.26-dev"
1734
+ },
1735
+ "thanks": {
1736
+ "name": "symfony/polyfill",
1737
+ "url": "https://github.com/symfony/polyfill"
1738
+ }
1739
+ },
1740
+ "autoload": {
1741
+ "files": [
1742
+ "bootstrap.php"
1743
+ ],
1744
+ "psr-4": {
1745
+ "Symfony\\Polyfill\\Uuid\\": ""
1746
+ }
1747
+ },
1748
+ "notification-url": "https://packagist.org/downloads/",
1749
+ "license": [
1750
+ "MIT"
1751
+ ],
1752
+ "authors": [
1753
+ {
1754
+ "name": "Grégoire Pineau",
1755
+ "email": "lyrixx@lyrixx.info"
1756
+ },
1757
+ {
1758
+ "name": "Symfony Community",
1759
+ "homepage": "https://symfony.com/contributors"
1760
+ }
1761
+ ],
1762
+ "description": "Symfony polyfill for uuid functions",
1763
+ "homepage": "https://symfony.com",
1764
+ "keywords": [
1765
+ "compatibility",
1766
+ "polyfill",
1767
+ "portable",
1768
+ "uuid"
1769
+ ],
1770
+ "support": {
1771
+ "source": "https://github.com/symfony/polyfill-uuid/tree/v1.26.0"
1772
+ },
1773
+ "funding": [
1774
+ {
1775
+ "url": "https://symfony.com/sponsor",
1776
+ "type": "custom"
1777
+ },
1778
+ {
1779
+ "url": "https://github.com/fabpot",
1780
+ "type": "github"
1781
+ },
1782
+ {
1783
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1784
+ "type": "tidelift"
1785
+ }
1786
+ ],
1787
+ "time": "2022-05-24T11:49:31+00:00"
1788
+ },
1789
+ {
1790
+ "name": "symfony/service-contracts",
1791
+ "version": "v2.5.1",
1792
+ "source": {
1793
+ "type": "git",
1794
+ "url": "https://github.com/symfony/service-contracts.git",
1795
+ "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c"
1796
+ },
1797
+ "dist": {
1798
+ "type": "zip",
1799
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
1800
+ "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
1801
+ "shasum": ""
1802
+ },
1803
+ "require": {
1804
+ "php": ">=7.2.5",
1805
+ "psr/container": "^1.1",
1806
+ "symfony/deprecation-contracts": "^2.1|^3"
1807
+ },
1808
+ "conflict": {
1809
+ "ext-psr": "<1.1|>=2"
1810
+ },
1811
+ "suggest": {
1812
+ "symfony/service-implementation": ""
1813
+ },
1814
+ "type": "library",
1815
+ "extra": {
1816
+ "branch-alias": {
1817
+ "dev-main": "2.5-dev"
1818
+ },
1819
+ "thanks": {
1820
+ "name": "symfony/contracts",
1821
+ "url": "https://github.com/symfony/contracts"
1822
+ }
1823
+ },
1824
+ "autoload": {
1825
+ "psr-4": {
1826
+ "Symfony\\Contracts\\Service\\": ""
1827
+ }
1828
+ },
1829
+ "notification-url": "https://packagist.org/downloads/",
1830
+ "license": [
1831
+ "MIT"
1832
+ ],
1833
+ "authors": [
1834
+ {
1835
+ "name": "Nicolas Grekas",
1836
+ "email": "p@tchwork.com"
1837
+ },
1838
+ {
1839
+ "name": "Symfony Community",
1840
+ "homepage": "https://symfony.com/contributors"
1841
+ }
1842
+ ],
1843
+ "description": "Generic abstractions related to writing services",
1844
+ "homepage": "https://symfony.com",
1845
+ "keywords": [
1846
+ "abstractions",
1847
+ "contracts",
1848
+ "decoupling",
1849
+ "interfaces",
1850
+ "interoperability",
1851
+ "standards"
1852
+ ],
1853
+ "support": {
1854
+ "source": "https://github.com/symfony/service-contracts/tree/v2.5.1"
1855
+ },
1856
+ "funding": [
1857
+ {
1858
+ "url": "https://symfony.com/sponsor",
1859
+ "type": "custom"
1860
+ },
1861
+ {
1862
+ "url": "https://github.com/fabpot",
1863
+ "type": "github"
1864
+ },
1865
+ {
1866
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1867
+ "type": "tidelift"
1868
+ }
1869
+ ],
1870
+ "time": "2022-03-13T20:07:29+00:00"
1871
+ }
1872
+ ],
1873
+ "packages-dev": [],
1874
+ "aliases": [],
1875
+ "minimum-stability": "stable",
1876
+ "stability-flags": [],
1877
+ "prefer-stable": false,
1878
+ "prefer-lowest": false,
1879
+ "platform": {
1880
+ "php": "^7.2|^8.0"
1881
+ },
1882
+ "platform-dev": [],
1883
+ "plugin-api-version": "2.3.0"
1884
+ }
src/utils/CnbAdminFunctions.php CHANGED
@@ -75,6 +75,17 @@ class CnbAdminFunctions {
75
  );
76
  }
77
 
 
 
 
 
 
 
 
 
 
 
 
78
  function cnb_get_condition_match_types() {
79
  return array(
80
  'SIMPLE' => 'Page path is:',
@@ -84,6 +95,17 @@ class CnbAdminFunctions {
84
  );
85
  }
86
 
 
 
 
 
 
 
 
 
 
 
 
87
  /**
88
  * @param array $original Array of "daysOfWeek", index 0 == Monday, values should be strings and contain "true"
89
  * in order to be evaulated correctly.
@@ -114,7 +136,7 @@ class CnbAdminFunctions {
114
  * <p><strong>NOTE: $body and $cta_pretext are NOT escaped and are assumed to be pre-escaped (or contain no User input)</strong></p>
115
  *
116
  * @param $color string
117
- * @param $headline string
118
  * @param $body string Assumed to be pre-escaped HTML, so this will not be (re)escaped
119
  * @param $icon string
120
  * @param $cta_pretext string Assumed to be pre-escaped HTML, so this will not be (re)escaped
@@ -130,7 +152,8 @@ class CnbAdminFunctions {
130
  <div class="cnb-promobox-header cnb-promobox-header-' . esc_attr( $color ) . '">
131
  <span class="dashicons dashicons-' . esc_attr( $icon ) . '"></span>
132
  <h2 class="hndle">' .
133
- esc_html( $headline )
 
134
  . '</h2>
135
  </div>
136
  <div class="inside">
75
  );
76
  }
77
 
78
+ function cnb_get_condition_types() {
79
+ return array(
80
+ 'URL' => 'URL',
81
+ 'GEO' => 'Geofencing',
82
+ );
83
+ }
84
+
85
+ /**
86
+ * These apply to URL only
87
+ * @return string[]
88
+ */
89
  function cnb_get_condition_match_types() {
90
  return array(
91
  'SIMPLE' => 'Page path is:',
95
  );
96
  }
97
 
98
+ /**
99
+ * These apply to GEO only
100
+ *
101
+ * @return string[]
102
+ */
103
+ function cnb_get_condition_match_types_geo() {
104
+ return array(
105
+ 'COUNTRY_CODE' => 'Country code is:',
106
+ );
107
+ }
108
+
109
  /**
110
  * @param array $original Array of "daysOfWeek", index 0 == Monday, values should be strings and contain "true"
111
  * in order to be evaulated correctly.
136
  * <p><strong>NOTE: $body and $cta_pretext are NOT escaped and are assumed to be pre-escaped (or contain no User input)</strong></p>
137
  *
138
  * @param $color string
139
+ * @param $headline string Assumed to be pre-escaped HTML (or static HTML), so this will not be (re)escaped
140
  * @param $body string Assumed to be pre-escaped HTML, so this will not be (re)escaped
141
  * @param $icon string
142
  * @param $cta_pretext string Assumed to be pre-escaped HTML, so this will not be (re)escaped
152
  <div class="cnb-promobox-header cnb-promobox-header-' . esc_attr( $color ) . '">
153
  <span class="dashicons dashicons-' . esc_attr( $icon ) . '"></span>
154
  <h2 class="hndle">' .
155
+ // phpcs:ignore WordPress.Security
156
+ $headline
157
  . '</h2>
158
  </div>
159
  <div class="inside">
src/utils/CnbUtils.php CHANGED
@@ -31,29 +31,6 @@ class CnbUtils {
31
  return null;
32
  }
33
 
34
- /**
35
- * After the upgrade (changelog) dialog is dismissed, the "cnb_update_<version>" action is fired.
36
- *
37
- * This action updates the internal version number, so we're ready for the next update.
38
- *
39
- * @return void
40
- */
41
- public static function update_version() {
42
- $utils = new CnbUtils();
43
- $utils->set_changelog_version();
44
- }
45
-
46
- private function set_changelog_version() {
47
- $cnb_options = get_option( 'cnb' );
48
- $updated_options = array_merge(
49
- $cnb_options,
50
- array(
51
- 'changelog_version' => CNB_VERSION
52
- )
53
- );
54
- update_option( 'cnb', $updated_options );
55
- }
56
-
57
  /**
58
  * Returns true if the `active` flag is set and is enabled
59
  *
@@ -279,4 +256,9 @@ class CnbUtils {
279
 
280
  return $default;
281
  }
 
 
 
 
 
282
  }
31
  return null;
32
  }
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  /**
35
  * Returns true if the `active` flag is set and is enabled
36
  *
256
 
257
  return $default;
258
  }
259
+
260
+ function is_reporting_enabled() {
261
+ $cnb_options = get_option( 'cnb' );
262
+ return ( key_exists( 'error_reporting', $cnb_options ) && $cnb_options['error_reporting'] );
263
+ }
264
  }
src/utils/class-cachehandler.php CHANGED
@@ -105,7 +105,7 @@ class CacheHandler {
105
  $cnb_options = get_option( 'cnb' );
106
  $cnb_utils = new CnbUtils();
107
  // This action doesn't work - since it requires some args and a nonce
108
- // do_action('admin_post_purge_cache');
109
 
110
  // However, we can simply call the function that clears the cache after a save
111
 
105
  $cnb_options = get_option( 'cnb' );
106
  $cnb_utils = new CnbUtils();
107
  // This action doesn't work - since it requires some args and a nonce
108
+ // do_action( 'admin_post_purge_cache' );
109
 
110
  // However, we can simply call the function that clears the cache after a save
111
 
src/utils/class-cnb-sentry.php ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\utils;
4
+
5
+ use cnb\admin\api\RemoteTrace;
6
+ use cnb\CnbFooter;
7
+ use Error;
8
+ use Sentry\SentrySdk;
9
+ use Sentry\Tracing\TransactionContext;
10
+
11
+ // don't load directly
12
+ defined( 'ABSPATH' ) || die( '-1' );
13
+
14
+ /**
15
+ * This sets up the Sentry integration for CallNowButton only.
16
+ *
17
+ * See https://packagist.org/packages/sentry/sentry
18
+ */
19
+ class Cnb_Sentry {
20
+
21
+ /**
22
+ * DSN used by CallNowButton to collect WordPress statistics
23
+ *
24
+ * @var string
25
+ */
26
+ private $dsn = 'https://272863c3742949d9a7f828ddb221bc01@o432725.ingest.sentry.io/6507738';
27
+
28
+ private $trace;
29
+ /**
30
+ * Initialize Sentry IF:
31
+ * - PHP > 7.2 (required by Sentry)
32
+ * - Nobody else integrated Sentry themselves (checked via the existence of a class called Sentry\SentrySdk)
33
+ * - The user gave their permission to collect errors and metrics
34
+ *
35
+ * @param $op_name string name of operation, usually __METHOD__
36
+ *
37
+ * @return void
38
+ */
39
+ function init($op_name = 'sender') {
40
+ try {
41
+ global $cnb_sentry_init;
42
+ if ( $cnb_sentry_init ) {
43
+ return;
44
+ }
45
+
46
+ $cnb_options = get_option( 'cnb' );
47
+ // Only do with permission AND PHP > 7 (since that's what Sentry requires)
48
+ if ( version_compare( PHP_VERSION, '7.4.0', '>=' )
49
+ && ! class_exists( 'Sentry\SentrySdk' )
50
+ && key_exists( 'error_reporting', $cnb_options )
51
+ && $cnb_options['error_reporting'] ) {
52
+ $this->init_real( $op_name );
53
+ }
54
+ } catch (Error $e) {
55
+ // Do not interrupt or break plugin functionality if Sentry for whatever reason does not load
56
+ $cnb_sentry_init = true;
57
+ }
58
+ }
59
+
60
+ private function init_real($op_name) {
61
+ global $cnb_sentry_init;
62
+ if ( $cnb_sentry_init ) {
63
+ return;
64
+ }
65
+ require_once dirname( __FILE__ ) . '/../vendor/autoload.php';
66
+ \Sentry\init(
67
+ [
68
+ 'dsn' => $this->dsn,
69
+ 'release' => CNB_VERSION,
70
+ 'environment' => WP_DEBUG ? 'development' : 'production',
71
+ ] );
72
+
73
+
74
+ self::setup_global_transaction($op_name);
75
+ $cnb_sentry_init = true;
76
+ }
77
+
78
+ private function set_scope() {
79
+ \Sentry\configureScope( function ( $scope ) {
80
+ global $wp_version;
81
+
82
+ $scope->setContext(
83
+ 'WordPress', [
84
+ 'version' => $wp_version,
85
+ ]);
86
+ } );
87
+ }
88
+
89
+ private function get_ip() {
90
+ if (isset($_SERVER['REMOTE_ADDR'])) {
91
+ return preg_replace(
92
+ '/[^\d., ]/', '',
93
+ // phpcs:ignore WordPress.Security
94
+ wp_unslash( $_SERVER['REMOTE_ADDR'] )
95
+ );
96
+ }
97
+ return 'ip unknown';
98
+ }
99
+
100
+ /**
101
+ * Finish the transaction, this submits the transaction and its span to Sentry
102
+ *
103
+ * @return void
104
+ */
105
+ public function finish() {
106
+ global $cnb_transaction;
107
+ if ( !$cnb_transaction ) {
108
+ return;
109
+ }
110
+ $this->trace = new RemoteTrace($this->get_endpoint(), __METHOD__);
111
+ $this->set_scope();
112
+ $cnb_transaction->finish();
113
+ $this->print_trace_for_footer();
114
+
115
+ }
116
+
117
+ private function get_endpoint() {
118
+ $dsn = \Sentry\Dsn::createFromString($this->dsn);
119
+ return $dsn->getScheme() . '://' . $dsn->getHost();
120
+ }
121
+
122
+ private function print_trace_for_footer() {
123
+ $this->trace->end();
124
+ $footer = new CnbFooter();
125
+ if ($footer->is_show_traces()) {
126
+ $footer->print_traces( [ $this->trace ] );
127
+ }
128
+ }
129
+
130
+ private function setup_global_transaction($op_name) {
131
+ global $cnb_transaction;
132
+ if ($cnb_transaction !== null) {
133
+ // Already setup!
134
+ return;
135
+ }
136
+
137
+ try {
138
+ // Setup context for the full transaction
139
+ $transactionContext = new TransactionContext();
140
+
141
+ $transactionContext->setName( 'cnb_wp_plugin' );
142
+ $transactionContext->setOp( $op_name );
143
+
144
+ // See if we can split $op_name by :: once
145
+ $parts = explode('::', $op_name, 2);
146
+ if (count($parts) === 2) {
147
+ list($name, $op) = $parts;
148
+ $transactionContext->setName( $name );
149
+ $transactionContext->setOp( $op );
150
+ }
151
+
152
+ // Start the transaction
153
+ $cnb_transaction = \Sentry\startTransaction( $transactionContext );
154
+
155
+ // Set the current transaction as the current span so we can retrieve it later
156
+ SentrySdk::getCurrentHub()->setSpan( $cnb_transaction );
157
+ } catch ( Error $exception ) {
158
+ \Sentry\captureException($exception);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * @return \Sentry\Tracing\Span
164
+ */
165
+ public static function start_span($name, $context) {
166
+ global $cnb_transaction;
167
+ if (!$cnb_transaction) {
168
+ return null;
169
+ }
170
+
171
+ try {
172
+ // Setup the context for the expensive operation span
173
+ $spanContext = new \Sentry\Tracing\SpanContext();
174
+ $spanContext->setOp( $context );
175
+
176
+ // Start the span
177
+ $span = $cnb_transaction->startChild( $spanContext );
178
+ $span->setDescription( $name );
179
+
180
+ // Set the current span to the span we just started
181
+ //SentrySdk::getCurrentHub()->setSpan( $span );
182
+
183
+ return $span;
184
+ } catch ( Error $exception ) {
185
+ try {
186
+ \Sentry\captureException($exception);
187
+ } catch ( Error $exception2 ) {
188
+ // NOOP
189
+ }
190
+
191
+ }
192
+ return null;
193
+ }
194
+
195
+ public static function finish_span($span) {
196
+ global $cnb_transaction;
197
+ if (!$cnb_transaction || !$span) {
198
+ return;
199
+ }
200
+
201
+ // Finish the span
202
+ $span->finish();
203
+ }
204
+ }
src/vendor/autoload.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ if (PHP_VERSION_ID < 50600) {
6
+ 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;
7
+ exit(1);
8
+ }
9
+
10
+ require_once __DIR__ . '/composer/autoload_real.php';
11
+
12
+ return ComposerAutoloaderInit447dd0acaa396d40f40a0cb37adffd44::getLoader();
src/vendor/clue/stream-filter/.github/FUNDING.yml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ github: clue
2
+ custom: https://clue.engineering/support
src/vendor/clue/stream-filter/CHANGELOG.md ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Changelog
2
+
3
+ ## 1.6.0 (2022-02-21)
4
+
5
+ * Feature: Support PHP 8.1 release.
6
+ (#45 by @clue)
7
+
8
+ * Improve documentation to use fully-qualified function names.
9
+ (#43 by @SimonFrings and #42 by @PaulRotmann)
10
+
11
+ * Improve test suite and use GitHub actions for continuous integration (CI).
12
+ (#39 and #40 by @SimonFrings)
13
+
14
+ ## 1.5.0 (2020-10-02)
15
+
16
+ * Feature: Improve performance by using global imports.
17
+ (#38 by @clue)
18
+
19
+ * Improve API documentation and add support / sponsorship info.
20
+ (#30 by @clue and #35 by @SimonFrings)
21
+
22
+ * Improve test suite and add `.gitattributes` to exclude dev files from exports.
23
+ Prepare PHP 8 support, update to PHPUnit 9 and simplify test matrix.
24
+ (#32 and #37 by @clue and #34 and #36 by @SimonFrings)
25
+
26
+ ## 1.4.1 (2019-04-09)
27
+
28
+ * Fix: Check if the function is declared before declaring it.
29
+ (#23 by @Niko9911)
30
+
31
+ * Improve test suite to also test against PHP 7.2 and
32
+ add test for base64 encoding and decoding filters.
33
+ (#22 by @arubacao and #25 by @Nyholm and @clue)
34
+
35
+ ## 1.4.0 (2017-08-18)
36
+
37
+ * Feature / Fix: The `fun()` function does not pass filter parameter `null`
38
+ to underlying `stream_filter_append()` by default
39
+ (#15 by @Nyholm)
40
+
41
+ Certain filters (such as `convert.quoted-printable-encode`) do not accept
42
+ a filter parameter at all. If no explicit filter parameter is given, we no
43
+ longer pass a default `null` value.
44
+
45
+ ```php
46
+ $encode = Filter\fun('convert.quoted-printable-encode');
47
+ assert('t=C3=A4st' === $encode('täst'));
48
+ ```
49
+
50
+ * Add examples and improve documentation
51
+ (#13 and #20 by @clue and #18 by @Nyholm)
52
+
53
+ * Improve test suite by adding PHPUnit to require-dev,
54
+ fix HHVM build for now again and ignore future HHVM build errors,
55
+ lock Travis distro so new future defaults will not break the build
56
+ and test on PHP 7.1
57
+ (#12, #14 and #19 by @clue and #16 by @Nyholm)
58
+
59
+ ## 1.3.0 (2015-11-08)
60
+
61
+ * Feature: Support accessing built-in filters as callbacks
62
+ (#5 by @clue)
63
+
64
+ ```php
65
+ $fun = Filter\fun('zlib.deflate');
66
+
67
+ $ret = $fun('hello') . $fun('world') . $fun();
68
+ assert('helloworld' === gzinflate($ret));
69
+ ```
70
+
71
+ ## 1.2.0 (2015-10-23)
72
+
73
+ * Feature: Invoke close event when closing filter (flush buffer)
74
+ (#9 by @clue)
75
+
76
+ ## 1.1.0 (2015-10-22)
77
+
78
+ * Feature: Abort filter operation when catching an Exception
79
+ (#10 by @clue)
80
+
81
+ * Feature: Additional safeguards to prevent filter state corruption
82
+ (#7 by @clue)
83
+
84
+ ## 1.0.0 (2015-10-18)
85
+
86
+ * First tagged release
src/vendor/clue/stream-filter/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Christian Lück
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is furnished
10
+ to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
src/vendor/clue/stream-filter/README.md ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # clue/stream-filter
2
+
3
+ [![CI status](https://github.com/clue/stream-filter/workflows/CI/badge.svg)](https://github.com/clue/stream-filter/actions)
4
+ [![installs on Packagist](https://img.shields.io/packagist/dt/clue/stream-filter?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/clue/stream-filter)
5
+
6
+ A simple and modern approach to stream filtering in PHP
7
+
8
+ **Table of contents**
9
+
10
+ * [Why?](#why)
11
+ * [Support us](#support-us)
12
+ * [Usage](#usage)
13
+ * [append()](#append)
14
+ * [prepend()](#prepend)
15
+ * [fun()](#fun)
16
+ * [remove()](#remove)
17
+ * [Install](#install)
18
+ * [Tests](#tests)
19
+ * [License](#license)
20
+
21
+ ## Why?
22
+
23
+ PHP's stream filtering system is great!
24
+
25
+ It offers very powerful stream filtering options and comes with a useful set of built-in filters.
26
+ These filters can be used to easily and efficiently perform various transformations on-the-fly, such as:
27
+
28
+ * read from a gzip'ed input file,
29
+ * transcode from ISO-8859-1 (Latin1) to UTF-8,
30
+ * write to a bzip output file
31
+ * and much more.
32
+
33
+ But let's face it:
34
+ Its API is [*difficult to work with*](https://www.php.net/manual/en/php-user-filter.filter.php)
35
+ and its documentation is [*subpar*](https://stackoverflow.com/questions/27103269/what-is-a-bucket-brigade).
36
+ This combined means its powerful features are often neglected.
37
+
38
+ This project aims to make these features more accessible to a broader audience.
39
+ * **Lightweight, SOLID design** -
40
+ Provides a thin abstraction that is [*just good enough*](https://en.wikipedia.org/wiki/Principle_of_good_enough)
41
+ and does not get in your way.
42
+ Custom filters require trivial effort.
43
+ * **Good test coverage** -
44
+ Comes with an automated tests suite and is regularly tested in the *real world*
45
+
46
+ ## Support us
47
+
48
+ We invest a lot of time developing, maintaining and updating our awesome
49
+ open-source projects. You can help us sustain this high-quality of our work by
50
+ [becoming a sponsor on GitHub](https://github.com/sponsors/clue). Sponsors get
51
+ numerous benefits in return, see our [sponsoring page](https://github.com/sponsors/clue)
52
+ for details.
53
+
54
+ Let's take these projects to the next level together! 🚀
55
+
56
+ ## Usage
57
+
58
+ This lightweight library consists only of a few simple functions.
59
+ All functions reside under the `Clue\StreamFilter` namespace.
60
+
61
+ The below examples refer to all functions with their fully-qualified names like this:
62
+
63
+ ```php
64
+ Clue\StreamFilter\append(…);
65
+ ```
66
+
67
+ As of PHP 5.6+ you can also import each required function into your code like this:
68
+
69
+ ```php
70
+ use function Clue\StreamFilter\append;
71
+
72
+ append(…);
73
+ ```
74
+
75
+ Alternatively, you can also use an import statement similar to this:
76
+
77
+ ```php
78
+ use Clue\StreamFilter as Filter;
79
+
80
+ Filter\append(…);
81
+ ```
82
+
83
+ ### append()
84
+
85
+ The `append(resource<stream> $stream, callable $callback, int $read_write = STREAM_FILTER_ALL): resource<stream filter>` function can be used to
86
+ append a filter callback to the given stream.
87
+
88
+ Each stream can have a list of filters attached.
89
+ This function appends a filter to the end of this list.
90
+
91
+ If the given filter can not be added, it throws an `Exception`.
92
+
93
+ The `$stream` can be any valid stream resource, such as:
94
+
95
+ ```php
96
+ $stream = fopen('demo.txt', 'w+');
97
+ ```
98
+
99
+ The `$callback` should be a valid callable function which accepts
100
+ an individual chunk of data and should return the updated chunk:
101
+
102
+ ```php
103
+ $filter = Clue\StreamFilter\append($stream, function ($chunk) {
104
+ // will be called each time you read or write a $chunk to/from the stream
105
+ return $chunk;
106
+ });
107
+ ```
108
+
109
+ As such, you can also use native PHP functions or any other `callable`:
110
+
111
+ ```php
112
+ Clue\StreamFilter\append($stream, 'strtoupper');
113
+
114
+ // will write "HELLO" to the underlying stream
115
+ fwrite($stream, 'hello');
116
+ ```
117
+
118
+ If the `$callback` accepts invocation without parameters,
119
+ then this signature will be invoked once ending (flushing) the filter:
120
+
121
+ ```php
122
+ Clue\StreamFilter\append($stream, function ($chunk = null) {
123
+ if ($chunk === null) {
124
+ // will be called once ending the filter
125
+ return 'end';
126
+ }
127
+ // will be called each time you read or write a $chunk to/from the stream
128
+ return $chunk;
129
+ });
130
+
131
+ fclose($stream);
132
+ ```
133
+
134
+ > Note: Legacy PHP versions (PHP < 5.4) do not support passing additional data
135
+ from the end signal handler if the stream is being closed.
136
+
137
+ If your callback throws an `Exception`, then the filter process will be aborted.
138
+ In order to play nice with PHP's stream handling,
139
+ the `Exception` will be transformed to a PHP warning instead:
140
+
141
+ ```php
142
+ Clue\StreamFilter\append($stream, function ($chunk) {
143
+ throw new \RuntimeException('Unexpected chunk');
144
+ });
145
+
146
+ // raises an E_USER_WARNING with "Error invoking filter: Unexpected chunk"
147
+ fwrite($stream, 'hello');
148
+ ```
149
+
150
+ The optional `$read_write` parameter can be used to only invoke the `$callback`
151
+ when either writing to the stream or only when reading from the stream:
152
+
153
+ ```php
154
+ Clue\StreamFilter\append($stream, function ($chunk) {
155
+ // will be called each time you write to the stream
156
+ return $chunk;
157
+ }, STREAM_FILTER_WRITE);
158
+
159
+ Clue\StreamFilter\append($stream, function ($chunk) {
160
+ // will be called each time you read from the stream
161
+ return $chunk;
162
+ }, STREAM_FILTER_READ);
163
+ ```
164
+
165
+ This function returns a filter resource which can be passed to [`remove()`](#remove).
166
+
167
+ > Note that once a filter has been added to stream, the stream can no longer be passed to
168
+ > [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php)
169
+ > (and family).
170
+ >
171
+ > > Warning: stream_select(): cannot cast a filtered stream on this system in {file} on line {line}
172
+ >
173
+ > This is due to limitations of PHP's stream filter support, as it can no longer reliably
174
+ > tell when the underlying stream resource is actually ready.
175
+ > As an alternative, consider calling `stream_select()` on the unfiltered stream and
176
+ > then pass the unfiltered data through the [`fun()`](#fun) function.
177
+
178
+ ### prepend()
179
+
180
+ The `prepend(resource<stream> $stream, callable $callback, int $read_write = STREAM_FILTER_ALL): resource<stream filter>` function can be used to
181
+ prepend a filter callback to the given stream.
182
+
183
+ Each stream can have a list of filters attached.
184
+ This function prepends a filter to the start of this list.
185
+
186
+ If the given filter can not be added, it throws an `Exception`.
187
+
188
+ ```php
189
+ $filter = Clue\StreamFilter\prepend($stream, function ($chunk) {
190
+ // will be called each time you read or write a $chunk to/from the stream
191
+ return $chunk;
192
+ });
193
+ ```
194
+
195
+ This function returns a filter resource which can be passed to [`remove()`](#remove).
196
+
197
+ Except for the position in the list of filters, this function behaves exactly
198
+ like the [`append()`](#append) function.
199
+ For more details about its behavior, see also the [`append()`](#append) function.
200
+
201
+ ### fun()
202
+
203
+ The `fun(string $filter, mixed $parameters = null): callable` function can be used to
204
+ create a filter function which uses the given built-in `$filter`.
205
+
206
+ PHP comes with a useful set of [built-in filters](https://www.php.net/manual/en/filters.php).
207
+ Using `fun()` makes accessing these as easy as passing an input string to filter
208
+ and getting the filtered output string.
209
+
210
+ ```php
211
+ $fun = Clue\StreamFilter\fun('string.rot13');
212
+
213
+ assert('grfg' === $fun('test'));
214
+ assert('test' === $fun($fun('test'));
215
+ ```
216
+
217
+ Please note that not all filter functions may be available depending
218
+ on installed PHP extensions and the PHP version in use.
219
+ In particular, [HHVM](https://hhvm.com/) may not offer the same filter functions
220
+ or parameters as Zend PHP.
221
+ Accessing an unknown filter function will result in a `RuntimeException`:
222
+
223
+ ```php
224
+ Clue\StreamFilter\fun('unknown'); // throws RuntimeException
225
+ ```
226
+
227
+ Some filters may accept or require additional filter parameters – most
228
+ filters do not require filter parameters.
229
+ If given, the optional `$parameters` argument will be passed to the
230
+ underlying filter handler as-is.
231
+ In particular, note how *not passing* this parameter at all differs from
232
+ explicitly passing a `null` value (which many filters do not accept).
233
+ Please refer to the individual filter definition for more details.
234
+ For example, the `string.strip_tags` filter can be invoked like this:
235
+
236
+ ```php
237
+ $fun = Clue\StreamFilter\fun('string.strip_tags', '<a><b>');
238
+
239
+ $ret = $fun('<b>h<br>i</b>');
240
+ assert('<b>hi</b>' === $ret);
241
+ ```
242
+
243
+ Under the hood, this function allocates a temporary memory stream, so it's
244
+ recommended to clean up the filter function after use.
245
+ Also, some filter functions (in particular the
246
+ [zlib compression filters](https://www.php.net/manual/en/filters.compression.php))
247
+ may use internal buffers and may emit a final data chunk on close.
248
+ The filter function can be closed by invoking without any arguments:
249
+
250
+ ```php
251
+ $fun = Clue\StreamFilter\fun('zlib.deflate');
252
+
253
+ $ret = $fun('hello') . $fun('world') . $fun();
254
+ assert('helloworld' === gzinflate($ret));
255
+ ```
256
+
257
+ The filter function must not be used anymore after it has been closed.
258
+ Doing so will result in a `RuntimeException`:
259
+
260
+ ```php
261
+ $fun = Clue\StreamFilter\fun('string.rot13');
262
+ $fun();
263
+
264
+ $fun('test'); // throws RuntimeException
265
+ ```
266
+
267
+ > Note: If you're using the zlib compression filters, then you should be wary
268
+ about engine inconsistencies between different PHP versions and HHVM.
269
+ These inconsistencies exist in the underlying PHP engines and there's little we
270
+ can do about this in this library.
271
+ [Our test suite](tests/) contains several test cases that exhibit these issues.
272
+ If you feel some test case is missing or outdated, we're happy to accept PRs! :)
273
+
274
+ ### remove()
275
+
276
+ The `remove(resource<stream filter> $filter): bool` function can be used to
277
+ remove a filter previously added via [`append()`](#append) or [`prepend()`](#prepend).
278
+
279
+ ```php
280
+ $filter = Clue\StreamFilter\append($stream, function () {
281
+ // …
282
+ });
283
+ Clue\StreamFilter\remove($filter);
284
+ ```
285
+
286
+ ## Install
287
+
288
+ The recommended way to install this library is [through Composer](https://getcomposer.org/).
289
+ [New to Composer?](https://getcomposer.org/doc/00-intro.md)
290
+
291
+ This project follows [SemVer](https://semver.org/).
292
+ This will install the latest supported version:
293
+
294
+ ```bash
295
+ $ composer require clue/stream-filter:^1.6
296
+ ```
297
+
298
+ See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
299
+
300
+ This project aims to run on any platform and thus does not require any PHP
301
+ extensions and supports running on legacy PHP 5.3 through current PHP 8+ and
302
+ HHVM.
303
+ It's *highly recommended to use the latest supported PHP version* for this project.
304
+ Older PHP versions may suffer from a number of inconsistencies documented above.
305
+
306
+ ## Tests
307
+
308
+ To run the test suite, you first need to clone this repo and then install all
309
+ dependencies [through Composer](https://getcomposer.org/):
310
+
311
+ ```bash
312
+ $ composer install
313
+ ```
314
+
315
+ To run the test suite, go to the project root and run:
316
+
317
+ ```bash
318
+ $ vendor/bin/phpunit
319
+ ```
320
+
321
+ ## License
322
+
323
+ This project is released under the permissive [MIT license](LICENSE).
324
+
325
+ > Did you know that I offer custom development services and issuing invoices for
326
+ sponsorships of releases and for contributions? Contact me (@clue) for details.
src/vendor/clue/stream-filter/composer.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "clue/stream-filter",
3
+ "description": "A simple and modern approach to stream filtering in PHP",
4
+ "keywords": ["stream", "callback", "filter", "php_user_filter", "stream_filter_append", "stream_filter_register", "bucket brigade"],
5
+ "homepage": "https://github.com/clue/php-stream-filter",
6
+ "license": "MIT",
7
+ "authors": [
8
+ {
9
+ "name": "Christian Lück",
10
+ "email": "christian@clue.engineering"
11
+ }
12
+ ],
13
+ "require": {
14
+ "php": ">=5.3"
15
+ },
16
+ "require-dev": {
17
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36"
18
+ },
19
+ "autoload": {
20
+ "psr-4": { "Clue\\StreamFilter\\": "src/" },
21
+ "files": [ "src/functions_include.php" ]
22
+ },
23
+ "autoload-dev": {
24
+ "psr-4": { "Clue\\Tests\\StreamFilter\\": "tests/" }
25
+ }
26
+ }
src/vendor/clue/stream-filter/src/CallbackFilter.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Clue\StreamFilter;
4
+
5
+ /**
6
+ * @internal
7
+ * @see append()
8
+ * @see prepend()
9
+ */
10
+ class CallbackFilter extends \php_user_filter
11
+ {
12
+ private $callback;
13
+ private $closed = true;
14
+ private $supportsClose = false;
15
+
16
+ /** @return bool */
17
+ #[\ReturnTypeWillChange]
18
+ public function onCreate()
19
+ {
20
+ $this->closed = false;
21
+
22
+ if (!\is_callable($this->params)) {
23
+ throw new \InvalidArgumentException('No valid callback parameter given to stream_filter_(append|prepend)');
24
+ }
25
+ $this->callback = $this->params;
26
+
27
+ // callback supports end event if it accepts invocation without arguments
28
+ $ref = new \ReflectionFunction($this->callback);
29
+ $this->supportsClose = ($ref->getNumberOfRequiredParameters() === 0);
30
+
31
+ return true;
32
+ }
33
+
34
+ /** @return void */
35
+ #[\ReturnTypeWillChange]
36
+ public function onClose()
37
+ {
38
+ $this->closed = true;
39
+
40
+ // callback supports closing and is not already closed
41
+ if ($this->supportsClose) {
42
+ $this->supportsClose = false;
43
+ // invoke without argument to signal end and discard resulting buffer
44
+ try {
45
+ \call_user_func($this->callback);
46
+ } catch (\Exception $ignored) {
47
+ // this might be called during engine shutdown, so it's not safe
48
+ // to raise any errors or exceptions here
49
+ // trigger_error('Error closing filter: ' . $ignored->getMessage(), E_USER_WARNING);
50
+ }
51
+ }
52
+
53
+ $this->callback = null;
54
+ }
55
+
56
+ /** @return int */
57
+ #[\ReturnTypeWillChange]
58
+ public function filter($in, $out, &$consumed, $closing)
59
+ {
60
+ // concatenate whole buffer from input brigade
61
+ $data = '';
62
+ while ($bucket = \stream_bucket_make_writeable($in)) {
63
+ $consumed += $bucket->datalen;
64
+ $data .= $bucket->data;
65
+ }
66
+
67
+ // skip processing callback that already ended
68
+ if ($this->closed) {
69
+ return \PSFS_FEED_ME;
70
+ }
71
+
72
+ // only invoke filter function if buffer is not empty
73
+ // this may skip flushing a closing filter
74
+ if ($data !== '') {
75
+ try {
76
+ $data = \call_user_func($this->callback, $data);
77
+ } catch (\Exception $e) {
78
+ // exception should mark filter as closed
79
+ $this->onClose();
80
+ \trigger_error('Error invoking filter: ' . $e->getMessage(), \E_USER_WARNING);
81
+
82
+ return \PSFS_ERR_FATAL;
83
+ }
84
+ }
85
+
86
+ // mark filter as closed after processing closing chunk
87
+ if ($closing) {
88
+ $this->closed = true;
89
+
90
+ // callback supports closing and is not already closed
91
+ if ($this->supportsClose) {
92
+ $this->supportsClose = false;
93
+
94
+ // invoke without argument to signal end and append resulting buffer
95
+ try {
96
+ $data .= \call_user_func($this->callback);
97
+ } catch (\Exception $e) {
98
+ \trigger_error('Error ending filter: ' . $e->getMessage(), \E_USER_WARNING);
99
+
100
+ return \PSFS_ERR_FATAL;
101
+ }
102
+ }
103
+ }
104
+
105
+ if ($data !== '') {
106
+ // create a new bucket for writing the resulting buffer to the output brigade
107
+ // reusing an existing bucket turned out to be bugged in some environments (ancient PHP versions and HHVM)
108
+ $bucket = @\stream_bucket_new($this->stream, $data);
109
+
110
+ // legacy PHP versions (PHP < 5.4) do not support passing data from the event signal handler
111
+ // because closing the stream invalidates the stream and its stream bucket brigade before
112
+ // invoking the filter close handler.
113
+ if ($bucket !== false) {
114
+ \stream_bucket_append($out, $bucket);
115
+ }
116
+ }
117
+
118
+ return \PSFS_PASS_ON;
119
+ }
120
+ }
src/vendor/clue/stream-filter/src/functions.php ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Clue\StreamFilter;
4
+
5
+ /**
6
+ * Append a filter callback to the given stream.
7
+ *
8
+ * Each stream can have a list of filters attached.
9
+ * This function appends a filter to the end of this list.
10
+ *
11
+ * If the given filter can not be added, it throws an `Exception`.
12
+ *
13
+ * The `$stream` can be any valid stream resource, such as:
14
+ *
15
+ * ```php
16
+ * $stream = fopen('demo.txt', 'w+');
17
+ * ```
18
+ *
19
+ * The `$callback` should be a valid callable function which accepts
20
+ * an individual chunk of data and should return the updated chunk:
21
+ *
22
+ * ```php
23
+ * $filter = Clue\StreamFilter\append($stream, function ($chunk) {
24
+ * // will be called each time you read or write a $chunk to/from the stream
25
+ * return $chunk;
26
+ * });
27
+ * ```
28
+ *
29
+ * As such, you can also use native PHP functions or any other `callable`:
30
+ *
31
+ * ```php
32
+ * Clue\StreamFilter\append($stream, 'strtoupper');
33
+ *
34
+ * // will write "HELLO" to the underlying stream
35
+ * fwrite($stream, 'hello');
36
+ * ```
37
+ *
38
+ * If the `$callback` accepts invocation without parameters,
39
+ * then this signature will be invoked once ending (flushing) the filter:
40
+ *
41
+ * ```php
42
+ * Clue\StreamFilter\append($stream, function ($chunk = null) {
43
+ * if ($chunk === null) {
44
+ * // will be called once ending the filter
45
+ * return 'end';
46
+ * }
47
+ * // will be called each time you read or write a $chunk to/from the stream
48
+ * return $chunk;
49
+ * });
50
+ *
51
+ * fclose($stream);
52
+ * ```
53
+ *
54
+ * > Note: Legacy PHP versions (PHP < 5.4) do not support passing additional data
55
+ * from the end signal handler if the stream is being closed.
56
+ *
57
+ * If your callback throws an `Exception`, then the filter process will be aborted.
58
+ * In order to play nice with PHP's stream handling,
59
+ * the `Exception` will be transformed to a PHP warning instead:
60
+ *
61
+ * ```php
62
+ * Clue\StreamFilter\append($stream, function ($chunk) {
63
+ * throw new \RuntimeException('Unexpected chunk');
64
+ * });
65
+ *
66
+ * // raises an E_USER_WARNING with "Error invoking filter: Unexpected chunk"
67
+ * fwrite($stream, 'hello');
68
+ * ```
69
+ *
70
+ * The optional `$read_write` parameter can be used to only invoke the `$callback`
71
+ * when either writing to the stream or only when reading from the stream:
72
+ *
73
+ * ```php
74
+ * Clue\StreamFilter\append($stream, function ($chunk) {
75
+ * // will be called each time you write to the stream
76
+ * return $chunk;
77
+ * }, STREAM_FILTER_WRITE);
78
+ *
79
+ * Clue\StreamFilter\append($stream, function ($chunk) {
80
+ * // will be called each time you read from the stream
81
+ * return $chunk;
82
+ * }, STREAM_FILTER_READ);
83
+ * ```
84
+ *
85
+ * This function returns a filter resource which can be passed to [`remove()`](#remove).
86
+ *
87
+ * > Note that once a filter has been added to stream, the stream can no longer be passed to
88
+ * > [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php)
89
+ * > (and family).
90
+ * >
91
+ * > > Warning: stream_select(): cannot cast a filtered stream on this system in {file} on line {line}
92
+ * >
93
+ * > This is due to limitations of PHP's stream filter support, as it can no longer reliably
94
+ * > tell when the underlying stream resource is actually ready.
95
+ * > As an alternative, consider calling `stream_select()` on the unfiltered stream and
96
+ * > then pass the unfiltered data through the [`fun()`](#fun) function.
97
+ *
98
+ * @param resource $stream
99
+ * @param callable $callback
100
+ * @param int $read_write
101
+ * @return resource filter resource which can be used for `remove()`
102
+ * @throws \Exception on error
103
+ * @uses stream_filter_append()
104
+ */
105
+ function append($stream, $callback, $read_write = STREAM_FILTER_ALL)
106
+ {
107
+ $ret = @\stream_filter_append($stream, register(), $read_write, $callback);
108
+
109
+ // PHP 8 throws above on type errors, older PHP and memory issues can throw here
110
+ // @codeCoverageIgnoreStart
111
+ if ($ret === false) {
112
+ $error = \error_get_last() + array('message' => '');
113
+ throw new \RuntimeException('Unable to append filter: ' . $error['message']);
114
+ }
115
+ // @codeCoverageIgnoreEnd
116
+
117
+ return $ret;
118
+ }
119
+
120
+ /**
121
+ * Prepend a filter callback to the given stream.
122
+ *
123
+ * Each stream can have a list of filters attached.
124
+ * This function prepends a filter to the start of this list.
125
+ *
126
+ * If the given filter can not be added, it throws an `Exception`.
127
+ *
128
+ * ```php
129
+ * $filter = Clue\StreamFilter\prepend($stream, function ($chunk) {
130
+ * // will be called each time you read or write a $chunk to/from the stream
131
+ * return $chunk;
132
+ * });
133
+ * ```
134
+ *
135
+ * This function returns a filter resource which can be passed to [`remove()`](#remove).
136
+ *
137
+ * Except for the position in the list of filters, this function behaves exactly
138
+ * like the [`append()`](#append) function.
139
+ * For more details about its behavior, see also the [`append()`](#append) function.
140
+ *
141
+ * @param resource $stream
142
+ * @param callable $callback
143
+ * @param int $read_write
144
+ * @return resource filter resource which can be used for `remove()`
145
+ * @throws \Exception on error
146
+ * @uses stream_filter_prepend()
147
+ */
148
+ function prepend($stream, $callback, $read_write = STREAM_FILTER_ALL)
149
+ {
150
+ $ret = @\stream_filter_prepend($stream, register(), $read_write, $callback);
151
+
152
+ // PHP 8 throws above on type errors, older PHP and memory issues can throw here
153
+ // @codeCoverageIgnoreStart
154
+ if ($ret === false) {
155
+ $error = \error_get_last() + array('message' => '');
156
+ throw new \RuntimeException('Unable to prepend filter: ' . $error['message']);
157
+ }
158
+ // @codeCoverageIgnoreEnd
159
+
160
+ return $ret;
161
+ }
162
+
163
+ /**
164
+ * Create a filter function which uses the given built-in `$filter`.
165
+ *
166
+ * PHP comes with a useful set of [built-in filters](https://www.php.net/manual/en/filters.php).
167
+ * Using `fun()` makes accessing these as easy as passing an input string to filter
168
+ * and getting the filtered output string.
169
+ *
170
+ * ```php
171
+ * $fun = Clue\StreamFilter\fun('string.rot13');
172
+ *
173
+ * assert('grfg' === $fun('test'));
174
+ * assert('test' === $fun($fun('test'));
175
+ * ```
176
+ *
177
+ * Please note that not all filter functions may be available depending
178
+ * on installed PHP extensions and the PHP version in use.
179
+ * In particular, [HHVM](https://hhvm.com/) may not offer the same filter functions
180
+ * or parameters as Zend PHP.
181
+ * Accessing an unknown filter function will result in a `RuntimeException`:
182
+ *
183
+ * ```php
184
+ * Clue\StreamFilter\fun('unknown'); // throws RuntimeException
185
+ * ```
186
+ *
187
+ * Some filters may accept or require additional filter parameters – most
188
+ * filters do not require filter parameters.
189
+ * If given, the optional `$parameters` argument will be passed to the
190
+ * underlying filter handler as-is.
191
+ * In particular, note how *not passing* this parameter at all differs from
192
+ * explicitly passing a `null` value (which many filters do not accept).
193
+ * Please refer to the individual filter definition for more details.
194
+ * For example, the `string.strip_tags` filter can be invoked like this:
195
+ *
196
+ * ```php
197
+ * $fun = Clue\StreamFilter\fun('string.strip_tags', '<a><b>');
198
+ *
199
+ * $ret = $fun('<b>h<br>i</b>');
200
+ * assert('<b>hi</b>' === $ret);
201
+ * ```
202
+ *
203
+ * Under the hood, this function allocates a temporary memory stream, so it's
204
+ * recommended to clean up the filter function after use.
205
+ * Also, some filter functions (in particular the
206
+ * [zlib compression filters](https://www.php.net/manual/en/filters.compression.php))
207
+ * may use internal buffers and may emit a final data chunk on close.
208
+ * The filter function can be closed by invoking without any arguments:
209
+ *
210
+ * ```php
211
+ * $fun = Clue\StreamFilter\fun('zlib.deflate');
212
+ *
213
+ * $ret = $fun('hello') . $fun('world') . $fun();
214
+ * assert('helloworld' === gzinflate($ret));
215
+ * ```
216
+ *
217
+ * The filter function must not be used anymore after it has been closed.
218
+ * Doing so will result in a `RuntimeException`:
219
+ *
220
+ * ```php
221
+ * $fun = Clue\StreamFilter\fun('string.rot13');
222
+ * $fun();
223
+ *
224
+ * $fun('test'); // throws RuntimeException
225
+ * ```
226
+ *
227
+ * > Note: If you're using the zlib compression filters, then you should be wary
228
+ * about engine inconsistencies between different PHP versions and HHVM.
229
+ * These inconsistencies exist in the underlying PHP engines and there's little we
230
+ * can do about this in this library.
231
+ * [Our test suite](tests/) contains several test cases that exhibit these issues.
232
+ * If you feel some test case is missing or outdated, we're happy to accept PRs! :)
233
+ *
234
+ * @param string $filter built-in filter name. See stream_get_filters() or http://php.net/manual/en/filters.php
235
+ * @param mixed $parameters (optional) parameters to pass to the built-in filter as-is
236
+ * @return callable a filter callback which can be append()'ed or prepend()'ed
237
+ * @throws \RuntimeException on error
238
+ * @link http://php.net/manual/en/filters.php
239
+ * @see stream_get_filters()
240
+ * @see append()
241
+ */
242
+ function fun($filter, $parameters = null)
243
+ {
244
+ $fp = \fopen('php://memory', 'w');
245
+ if (\func_num_args() === 1) {
246
+ $filter = @\stream_filter_append($fp, $filter, \STREAM_FILTER_WRITE);
247
+ } else {
248
+ $filter = @\stream_filter_append($fp, $filter, \STREAM_FILTER_WRITE, $parameters);
249
+ }
250
+
251
+ if ($filter === false) {
252
+ \fclose($fp);
253
+ $error = \error_get_last() + array('message' => '');
254
+ throw new \RuntimeException('Unable to access built-in filter: ' . $error['message']);
255
+ }
256
+
257
+ // append filter function which buffers internally
258
+ $buffer = '';
259
+ append($fp, function ($chunk) use (&$buffer) {
260
+ $buffer .= $chunk;
261
+
262
+ // always return empty string in order to skip actually writing to stream resource
263
+ return '';
264
+ }, \STREAM_FILTER_WRITE);
265
+
266
+ $closed = false;
267
+
268
+ return function ($chunk = null) use ($fp, $filter, &$buffer, &$closed) {
269
+ if ($closed) {
270
+ throw new \RuntimeException('Unable to perform operation on closed stream');
271
+ }
272
+ if ($chunk === null) {
273
+ $closed = true;
274
+ $buffer = '';
275
+ \fclose($fp);
276
+ return $buffer;
277
+ }
278
+ // initialize buffer and invoke filters by attempting to write to stream
279
+ $buffer = '';
280
+ \fwrite($fp, $chunk);
281
+
282
+ // buffer now contains everything the filter function returned
283
+ return $buffer;
284
+ };
285
+ }
286
+
287
+ /**
288
+ * Remove a filter previously added via `append()` or `prepend()`.
289
+ *
290
+ * ```php
291
+ * $filter = Clue\StreamFilter\append($stream, function () {
292
+ * // …
293
+ * });
294
+ * Clue\StreamFilter\remove($filter);
295
+ * ```
296
+ *
297
+ * @param resource $filter
298
+ * @return bool true on success or false on error
299
+ * @throws \RuntimeException on error
300
+ * @uses stream_filter_remove()
301
+ */
302
+ function remove($filter)
303
+ {
304
+ if (@\stream_filter_remove($filter) === false) {
305
+ // PHP 8 throws above on type errors, older PHP and memory issues can throw here
306
+ $error = \error_get_last();
307
+ throw new \RuntimeException('Unable to remove filter: ' . $error['message']);
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Registers the callback filter and returns the resulting filter name
313
+ *
314
+ * There should be little reason to call this function manually.
315
+ *
316
+ * @return string filter name
317
+ * @uses CallbackFilter
318
+ */
319
+ function register()
320
+ {
321
+ static $registered = null;
322
+ if ($registered === null) {
323
+ $registered = 'stream-callback';
324
+ \stream_filter_register($registered, __NAMESPACE__ . '\CallbackFilter');
325
+ }
326
+ return $registered;
327
+ }
src/vendor/clue/stream-filter/src/functions_include.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // @codeCoverageIgnoreStart
4
+ if (!\function_exists('Clue\\StreamFilter\\append')) {
5
+ require __DIR__ . '/functions.php';
6
+ }
src/vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,572 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see https://www.php-fig.org/psr/psr-0/
41
+ * @see https://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ /** @var ?string */
46
+ private $vendorDir;
47
+
48
+ // PSR-4
49
+ /**
50
+ * @var array[]
51
+ * @psalm-var array<string, array<string, int>>
52
+ */
53
+ private $prefixLengthsPsr4 = array();
54
+ /**
55
+ * @var array[]
56
+ * @psalm-var array<string, array<int, string>>
57
+ */
58
+ private $prefixDirsPsr4 = array();
59
+ /**
60
+ * @var array[]
61
+ * @psalm-var array<string, string>
62
+ */
63
+ private $fallbackDirsPsr4 = array();
64
+
65
+ // PSR-0
66
+ /**
67
+ * @var array[]
68
+ * @psalm-var array<string, array<string, string[]>>
69
+ */
70
+ private $prefixesPsr0 = array();
71
+ /**
72
+ * @var array[]
73
+ * @psalm-var array<string, string>
74
+ */
75
+ private $fallbackDirsPsr0 = array();
76
+
77
+ /** @var bool */
78
+ private $useIncludePath = false;
79
+
80
+ /**
81
+ * @var string[]
82
+ * @psalm-var array<string, string>
83
+ */
84
+ private $classMap = array();
85
+
86
+ /** @var bool */
87
+ private $classMapAuthoritative = false;
88
+
89
+ /**
90
+ * @var bool[]
91
+ * @psalm-var array<string, bool>
92
+ */
93
+ private $missingClasses = array();
94
+
95
+ /** @var ?string */
96
+ private $apcuPrefix;
97
+
98
+ /**
99
+ * @var self[]
100
+ */
101
+ private static $registeredLoaders = array();
102
+
103
+ /**
104
+ * @param ?string $vendorDir
105
+ */
106
+ public function __construct($vendorDir = null)
107
+ {
108
+ $this->vendorDir = $vendorDir;
109
+ }
110
+
111
+ /**
112
+ * @return string[]
113
+ */
114
+ public function getPrefixes()
115
+ {
116
+ if (!empty($this->prefixesPsr0)) {
117
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
118
+ }
119
+
120
+ return array();
121
+ }
122
+
123
+ /**
124
+ * @return array[]
125
+ * @psalm-return array<string, array<int, string>>
126
+ */
127
+ public function getPrefixesPsr4()
128
+ {
129
+ return $this->prefixDirsPsr4;
130
+ }
131
+
132
+ /**
133
+ * @return array[]
134
+ * @psalm-return array<string, string>
135
+ */
136
+ public function getFallbackDirs()
137
+ {
138
+ return $this->fallbackDirsPsr0;
139
+ }
140
+
141
+ /**
142
+ * @return array[]
143
+ * @psalm-return array<string, string>
144
+ */
145
+ public function getFallbackDirsPsr4()
146
+ {
147
+ return $this->fallbackDirsPsr4;
148
+ }
149
+
150
+ /**
151
+ * @return string[] Array of classname => path
152
+ * @psalm-return array<string, string>
153
+ */
154
+ public function getClassMap()
155
+ {
156
+ return $this->classMap;
157
+ }
158
+
159
+ /**
160
+ * @param string[] $classMap Class to filename map
161
+ * @psalm-param array<string, string> $classMap
162
+ *
163
+ * @return void
164
+ */
165
+ public function addClassMap(array $classMap)
166
+ {
167
+ if ($this->classMap) {
168
+ $this->classMap = array_merge($this->classMap, $classMap);
169
+ } else {
170
+ $this->classMap = $classMap;
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Registers a set of PSR-0 directories for a given prefix, either
176
+ * appending or prepending to the ones previously set for this prefix.
177
+ *
178
+ * @param string $prefix The prefix
179
+ * @param string[]|string $paths The PSR-0 root directories
180
+ * @param bool $prepend Whether to prepend the directories
181
+ *
182
+ * @return void
183
+ */
184
+ public function add($prefix, $paths, $prepend = false)
185
+ {
186
+ if (!$prefix) {
187
+ if ($prepend) {
188
+ $this->fallbackDirsPsr0 = array_merge(
189
+ (array) $paths,
190
+ $this->fallbackDirsPsr0
191
+ );
192
+ } else {
193
+ $this->fallbackDirsPsr0 = array_merge(
194
+ $this->fallbackDirsPsr0,
195
+ (array) $paths
196
+ );
197
+ }
198
+
199
+ return;
200
+ }
201
+
202
+ $first = $prefix[0];
203
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
204
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
205
+
206
+ return;
207
+ }
208
+ if ($prepend) {
209
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
210
+ (array) $paths,
211
+ $this->prefixesPsr0[$first][$prefix]
212
+ );
213
+ } else {
214
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
215
+ $this->prefixesPsr0[$first][$prefix],
216
+ (array) $paths
217
+ );
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Registers a set of PSR-4 directories for a given namespace, either
223
+ * appending or prepending to the ones previously set for this namespace.
224
+ *
225
+ * @param string $prefix The prefix/namespace, with trailing '\\'
226
+ * @param string[]|string $paths The PSR-4 base directories
227
+ * @param bool $prepend Whether to prepend the directories
228
+ *
229
+ * @throws \InvalidArgumentException
230
+ *
231
+ * @return void
232
+ */
233
+ public function addPsr4($prefix, $paths, $prepend = false)
234
+ {
235
+ if (!$prefix) {
236
+ // Register directories for the root namespace.
237
+ if ($prepend) {
238
+ $this->fallbackDirsPsr4 = array_merge(
239
+ (array) $paths,
240
+ $this->fallbackDirsPsr4
241
+ );
242
+ } else {
243
+ $this->fallbackDirsPsr4 = array_merge(
244
+ $this->fallbackDirsPsr4,
245
+ (array) $paths
246
+ );
247
+ }
248
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
249
+ // Register directories for a new namespace.
250
+ $length = strlen($prefix);
251
+ if ('\\' !== $prefix[$length - 1]) {
252
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
253
+ }
254
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
255
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
256
+ } elseif ($prepend) {
257
+ // Prepend directories for an already registered namespace.
258
+ $this->prefixDirsPsr4[$prefix] = array_merge(
259
+ (array) $paths,
260
+ $this->prefixDirsPsr4[$prefix]
261
+ );
262
+ } else {
263
+ // Append directories for an already registered namespace.
264
+ $this->prefixDirsPsr4[$prefix] = array_merge(
265
+ $this->prefixDirsPsr4[$prefix],
266
+ (array) $paths
267
+ );
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Registers a set of PSR-0 directories for a given prefix,
273
+ * replacing any others previously set for this prefix.
274
+ *
275
+ * @param string $prefix The prefix
276
+ * @param string[]|string $paths The PSR-0 base directories
277
+ *
278
+ * @return void
279
+ */
280
+ public function set($prefix, $paths)
281
+ {
282
+ if (!$prefix) {
283
+ $this->fallbackDirsPsr0 = (array) $paths;
284
+ } else {
285
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Registers a set of PSR-4 directories for a given namespace,
291
+ * replacing any others previously set for this namespace.
292
+ *
293
+ * @param string $prefix The prefix/namespace, with trailing '\\'
294
+ * @param string[]|string $paths The PSR-4 base directories
295
+ *
296
+ * @throws \InvalidArgumentException
297
+ *
298
+ * @return void
299
+ */
300
+ public function setPsr4($prefix, $paths)
301
+ {
302
+ if (!$prefix) {
303
+ $this->fallbackDirsPsr4 = (array) $paths;
304
+ } else {
305
+ $length = strlen($prefix);
306
+ if ('\\' !== $prefix[$length - 1]) {
307
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
308
+ }
309
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
310
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Turns on searching the include path for class files.
316
+ *
317
+ * @param bool $useIncludePath
318
+ *
319
+ * @return void
320
+ */
321
+ public function setUseIncludePath($useIncludePath)
322
+ {
323
+ $this->useIncludePath = $useIncludePath;
324
+ }
325
+
326
+ /**
327
+ * Can be used to check if the autoloader uses the include path to check
328
+ * for classes.
329
+ *
330
+ * @return bool
331
+ */
332
+ public function getUseIncludePath()
333
+ {
334
+ return $this->useIncludePath;
335
+ }
336
+
337
+ /**
338
+ * Turns off searching the prefix and fallback directories for classes
339
+ * that have not been registered with the class map.
340
+ *
341
+ * @param bool $classMapAuthoritative
342
+ *
343
+ * @return void
344
+ */
345
+ public function setClassMapAuthoritative($classMapAuthoritative)
346
+ {
347
+ $this->classMapAuthoritative = $classMapAuthoritative;
348
+ }
349
+
350
+ /**
351
+ * Should class lookup fail if not found in the current class map?
352
+ *
353
+ * @return bool
354
+ */
355
+ public function isClassMapAuthoritative()
356
+ {
357
+ return $this->classMapAuthoritative;
358
+ }
359
+
360
+ /**
361
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
362
+ *
363
+ * @param string|null $apcuPrefix
364
+ *
365
+ * @return void
366
+ */
367
+ public function setApcuPrefix($apcuPrefix)
368
+ {
369
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
370
+ }
371
+
372
+ /**
373
+ * The APCu prefix in use, or null if APCu caching is not enabled.
374
+ *
375
+ * @return string|null
376
+ */
377
+ public function getApcuPrefix()
378
+ {
379
+ return $this->apcuPrefix;
380
+ }
381
+
382
+ /**
383
+ * Registers this instance as an autoloader.
384
+ *
385
+ * @param bool $prepend Whether to prepend the autoloader or not
386
+ *
387
+ * @return void
388
+ */
389
+ public function register($prepend = false)
390
+ {
391
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
392
+
393
+ if (null === $this->vendorDir) {
394
+ return;
395
+ }
396
+
397
+ if ($prepend) {
398
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
399
+ } else {
400
+ unset(self::$registeredLoaders[$this->vendorDir]);
401
+ self::$registeredLoaders[$this->vendorDir] = $this;
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Unregisters this instance as an autoloader.
407
+ *
408
+ * @return void
409
+ */
410
+ public function unregister()
411
+ {
412
+ spl_autoload_unregister(array($this, 'loadClass'));
413
+
414
+ if (null !== $this->vendorDir) {
415
+ unset(self::$registeredLoaders[$this->vendorDir]);
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Loads the given class or interface.
421
+ *
422
+ * @param string $class The name of the class
423
+ * @return true|null True if loaded, null otherwise
424
+ */
425
+ public function loadClass($class)
426
+ {
427
+ if ($file = $this->findFile($class)) {
428
+ includeFile($file);
429
+
430
+ return true;
431
+ }
432
+
433
+ return null;
434
+ }
435
+
436
+ /**
437
+ * Finds the path to the file where the class is defined.
438
+ *
439
+ * @param string $class The name of the class
440
+ *
441
+ * @return string|false The path if found, false otherwise
442
+ */
443
+ public function findFile($class)
444
+ {
445
+ // class map lookup
446
+ if (isset($this->classMap[$class])) {
447
+ return $this->classMap[$class];
448
+ }
449
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
450
+ return false;
451
+ }
452
+ if (null !== $this->apcuPrefix) {
453
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
454
+ if ($hit) {
455
+ return $file;
456
+ }
457
+ }
458
+
459
+ $file = $this->findFileWithExtension($class, '.php');
460
+
461
+ // Search for Hack files if we are running on HHVM
462
+ if (false === $file && defined('HHVM_VERSION')) {
463
+ $file = $this->findFileWithExtension($class, '.hh');
464
+ }
465
+
466
+ if (null !== $this->apcuPrefix) {
467
+ apcu_add($this->apcuPrefix.$class, $file);
468
+ }
469
+
470
+ if (false === $file) {
471
+ // Remember that this class does not exist.
472
+ $this->missingClasses[$class] = true;
473
+ }
474
+
475
+ return $file;
476
+ }
477
+
478
+ /**
479
+ * Returns the currently registered loaders indexed by their corresponding vendor directories.
480
+ *
481
+ * @return self[]
482
+ */
483
+ public static function getRegisteredLoaders()
484
+ {
485
+ return self::$registeredLoaders;
486
+ }
487
+
488
+ /**
489
+ * @param string $class
490
+ * @param string $ext
491
+ * @return string|false
492
+ */
493
+ private function findFileWithExtension($class, $ext)
494
+ {
495
+ // PSR-4 lookup
496
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
497
+
498
+ $first = $class[0];
499
+ if (isset($this->prefixLengthsPsr4[$first])) {
500
+ $subPath = $class;
501
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
502
+ $subPath = substr($subPath, 0, $lastPos);
503
+ $search = $subPath . '\\';
504
+ if (isset($this->prefixDirsPsr4[$search])) {
505
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
506
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
507
+ if (file_exists($file = $dir . $pathEnd)) {
508
+ return $file;
509
+ }
510
+ }
511
+ }
512
+ }
513
+ }
514
+
515
+ // PSR-4 fallback dirs
516
+ foreach ($this->fallbackDirsPsr4 as $dir) {
517
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
518
+ return $file;
519
+ }
520
+ }
521
+
522
+ // PSR-0 lookup
523
+ if (false !== $pos = strrpos($class, '\\')) {
524
+ // namespaced class name
525
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
526
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
527
+ } else {
528
+ // PEAR-like class name
529
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
530
+ }
531
+
532
+ if (isset($this->prefixesPsr0[$first])) {
533
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
534
+ if (0 === strpos($class, $prefix)) {
535
+ foreach ($dirs as $dir) {
536
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
537
+ return $file;
538
+ }
539
+ }
540
+ }
541
+ }
542
+ }
543
+
544
+ // PSR-0 fallback dirs
545
+ foreach ($this->fallbackDirsPsr0 as $dir) {
546
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
547
+ return $file;
548
+ }
549
+ }
550
+
551
+ // PSR-0 include paths.
552
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
553
+ return $file;
554
+ }
555
+
556
+ return false;
557
+ }
558
+ }
559
+
560
+ /**
561
+ * Scope isolated include.
562
+ *
563
+ * Prevents access to $this/self from included files.
564
+ *
565
+ * @param string $file
566
+ * @return void
567
+ * @private
568
+ */
569
+ function includeFile($file)
570
+ {
571
+ include $file;
572
+ }
src/vendor/composer/InstalledVersions.php ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer;
14
+
15
+ use Composer\Autoload\ClassLoader;
16
+ use Composer\Semver\VersionParser;
17
+
18
+ /**
19
+ * This class is copied in every Composer installed project and available to all
20
+ *
21
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22
+ *
23
+ * To require its presence, you can require `composer-runtime-api ^2.0`
24
+ *
25
+ * @final
26
+ */
27
+ class InstalledVersions
28
+ {
29
+ /**
30
+ * @var mixed[]|null
31
+ * @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
32
+ */
33
+ private static $installed;
34
+
35
+ /**
36
+ * @var bool|null
37
+ */
38
+ private static $canGetVendors;
39
+
40
+ /**
41
+ * @var array[]
42
+ * @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}>}>
43
+ */
44
+ private static $installedByVendor = array();
45
+
46
+ /**
47
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
48
+ *
49
+ * @return string[]
50
+ * @psalm-return list<string>
51
+ */
52
+ public static function getInstalledPackages()
53
+ {
54
+ $packages = array();
55
+ foreach (self::getInstalled() as $installed) {
56
+ $packages[] = array_keys($installed['versions']);
57
+ }
58
+
59
+ if (1 === \count($packages)) {
60
+ return $packages[0];
61
+ }
62
+
63
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
64
+ }
65
+
66
+ /**
67
+ * Returns a list of all package names with a specific type e.g. 'library'
68
+ *
69
+ * @param string $type
70
+ * @return string[]
71
+ * @psalm-return list<string>
72
+ */
73
+ public static function getInstalledPackagesByType($type)
74
+ {
75
+ $packagesByType = array();
76
+
77
+ foreach (self::getInstalled() as $installed) {
78
+ foreach ($installed['versions'] as $name => $package) {
79
+ if (isset($package['type']) && $package['type'] === $type) {
80
+ $packagesByType[] = $name;
81
+ }
82
+ }
83
+ }
84
+
85
+ return $packagesByType;
86
+ }
87
+
88
+ /**
89
+ * Checks whether the given package is installed
90
+ *
91
+ * This also returns true if the package name is provided or replaced by another package
92
+ *
93
+ * @param string $packageName
94
+ * @param bool $includeDevRequirements
95
+ * @return bool
96
+ */
97
+ public static function isInstalled($packageName, $includeDevRequirements = true)
98
+ {
99
+ foreach (self::getInstalled() as $installed) {
100
+ if (isset($installed['versions'][$packageName])) {
101
+ return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
102
+ }
103
+ }
104
+
105
+ return false;
106
+ }
107
+
108
+ /**
109
+ * Checks whether the given package satisfies a version constraint
110
+ *
111
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
112
+ *
113
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
114
+ *
115
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
116
+ * @param string $packageName
117
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
118
+ * @return bool
119
+ */
120
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
121
+ {
122
+ $constraint = $parser->parseConstraints($constraint);
123
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
124
+
125
+ return $provided->matches($constraint);
126
+ }
127
+
128
+ /**
129
+ * Returns a version constraint representing all the range(s) which are installed for a given package
130
+ *
131
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
132
+ * whether a given version of a package is installed, and not just whether it exists
133
+ *
134
+ * @param string $packageName
135
+ * @return string Version constraint usable with composer/semver
136
+ */
137
+ public static function getVersionRanges($packageName)
138
+ {
139
+ foreach (self::getInstalled() as $installed) {
140
+ if (!isset($installed['versions'][$packageName])) {
141
+ continue;
142
+ }
143
+
144
+ $ranges = array();
145
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
146
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
147
+ }
148
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
149
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
150
+ }
151
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
152
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
153
+ }
154
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
155
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
156
+ }
157
+
158
+ return implode(' || ', $ranges);
159
+ }
160
+
161
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
162
+ }
163
+
164
+ /**
165
+ * @param string $packageName
166
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
167
+ */
168
+ public static function getVersion($packageName)
169
+ {
170
+ foreach (self::getInstalled() as $installed) {
171
+ if (!isset($installed['versions'][$packageName])) {
172
+ continue;
173
+ }
174
+
175
+ if (!isset($installed['versions'][$packageName]['version'])) {
176
+ return null;
177
+ }
178
+
179
+ return $installed['versions'][$packageName]['version'];
180
+ }
181
+
182
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
183
+ }
184
+
185
+ /**
186
+ * @param string $packageName
187
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
188
+ */
189
+ public static function getPrettyVersion($packageName)
190
+ {
191
+ foreach (self::getInstalled() as $installed) {
192
+ if (!isset($installed['versions'][$packageName])) {
193
+ continue;
194
+ }
195
+
196
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
197
+ return null;
198
+ }
199
+
200
+ return $installed['versions'][$packageName]['pretty_version'];
201
+ }
202
+
203
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
204
+ }
205
+
206
+ /**
207
+ * @param string $packageName
208
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
209
+ */
210
+ public static function getReference($packageName)
211
+ {
212
+ foreach (self::getInstalled() as $installed) {
213
+ if (!isset($installed['versions'][$packageName])) {
214
+ continue;
215
+ }
216
+
217
+ if (!isset($installed['versions'][$packageName]['reference'])) {
218
+ return null;
219
+ }
220
+
221
+ return $installed['versions'][$packageName]['reference'];
222
+ }
223
+
224
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
225
+ }
226
+
227
+ /**
228
+ * @param string $packageName
229
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
230
+ */
231
+ public static function getInstallPath($packageName)
232
+ {
233
+ foreach (self::getInstalled() as $installed) {
234
+ if (!isset($installed['versions'][$packageName])) {
235
+ continue;
236
+ }
237
+
238
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
239
+ }
240
+
241
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
242
+ }
243
+
244
+ /**
245
+ * @return array
246
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
247
+ */
248
+ public static function getRootPackage()
249
+ {
250
+ $installed = self::getInstalled();
251
+
252
+ return $installed[0]['root'];
253
+ }
254
+
255
+ /**
256
+ * Returns the raw installed.php data for custom implementations
257
+ *
258
+ * @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.
259
+ * @return array[]
260
+ * @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}>}
261
+ */
262
+ public static function getRawData()
263
+ {
264
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
265
+
266
+ if (null === self::$installed) {
267
+ // only require the installed.php file if this file is loaded from its dumped location,
268
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
269
+ if (substr(__DIR__, -8, 1) !== 'C' && is_file(__DIR__ . '/installed.php')) {
270
+ self::$installed = include __DIR__ . '/installed.php';
271
+ } else {
272
+ self::$installed = array();
273
+ }
274
+ }
275
+
276
+ return self::$installed;
277
+ }
278
+
279
+ /**
280
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
281
+ *
282
+ * @return array[]
283
+ * @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}>}>
284
+ */
285
+ public static function getAllRawData()
286
+ {
287
+ return self::getInstalled();
288
+ }
289
+
290
+ /**
291
+ * Lets you reload the static array from another file
292
+ *
293
+ * This is only useful for complex integrations in which a project needs to use
294
+ * this class but then also needs to execute another project's autoloader in process,
295
+ * and wants to ensure both projects have access to their version of installed.php.
296
+ *
297
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
298
+ * the data it needs from this class, then call reload() with
299
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
300
+ * the project in which it runs can then also use this class safely, without
301
+ * interference between PHPUnit's dependencies and the project's dependencies.
302
+ *
303
+ * @param array[] $data A vendor/composer/installed.php data set
304
+ * @return void
305
+ *
306
+ * @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
307
+ */
308
+ public static function reload($data)
309
+ {
310
+ self::$installed = $data;
311
+ self::$installedByVendor = array();
312
+ }
313
+
314
+ /**
315
+ * @return array[]
316
+ * @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}>}>
317
+ */
318
+ private static function getInstalled()
319
+ {
320
+ if (null === self::$canGetVendors) {
321
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
322
+ }
323
+
324
+ $installed = array();
325
+
326
+ if (self::$canGetVendors) {
327
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
328
+ if (isset(self::$installedByVendor[$vendorDir])) {
329
+ $installed[] = self::$installedByVendor[$vendorDir];
330
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
331
+ $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
332
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
333
+ self::$installed = $installed[count($installed) - 1];
334
+ }
335
+ }
336
+ }
337
+ }
338
+
339
+ if (null === self::$installed) {
340
+ // only require the installed.php file if this file is loaded from its dumped location,
341
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
342
+ if (substr(__DIR__, -8, 1) !== 'C' && is_file(__DIR__ . '/installed.php')) {
343
+ self::$installed = require __DIR__ . '/installed.php';
344
+ } else {
345
+ self::$installed = array();
346
+ }
347
+ }
348
+ $installed[] = self::$installed;
349
+
350
+ return $installed;
351
+ }
352
+ }
src/vendor/composer/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) Nils Adermann, Jordi Boggiano
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
src/vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(__DIR__);
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
10
+ 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
11
+ 'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
12
+ 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
13
+ 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
14
+ 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
15
+ 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
16
+ );
src/vendor/composer/autoload_files.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_files.php @generated by Composer
4
+
5
+ $vendorDir = dirname(__DIR__);
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
10
+ '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
11
+ '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
12
+ '9c67151ae59aff4788964ce8eb2a0f43' => $vendorDir . '/clue/stream-filter/src/functions_include.php',
13
+ '8cff32064859f4559445b89279f3199c' => $vendorDir . '/php-http/message/src/filters.php',
14
+ '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
15
+ 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
16
+ '09f6b20656683369174dd6fa83b7e5fb' => $vendorDir . '/symfony/polyfill-uuid/bootstrap.php',
17
+ 'fb4ca2d97fe7ba6af750497425204e70' => $vendorDir . '/sentry/sentry/src/functions.php',
18
+ );
src/vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(__DIR__);
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
src/vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(__DIR__);
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Symfony\\Polyfill\\Uuid\\' => array($vendorDir . '/symfony/polyfill-uuid'),
10
+ 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
11
+ 'Symfony\\Polyfill\\Php73\\' => array($vendorDir . '/symfony/polyfill-php73'),
12
+ 'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
13
+ 'Symfony\\Contracts\\HttpClient\\' => array($vendorDir . '/symfony/http-client-contracts'),
14
+ 'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
15
+ 'Symfony\\Component\\HttpClient\\' => array($vendorDir . '/symfony/http-client'),
16
+ 'Sentry\\' => array($vendorDir . '/sentry/sentry/src'),
17
+ 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
18
+ 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'),
19
+ 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
20
+ 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
21
+ 'Jean85\\' => array($vendorDir . '/jean85/pretty-package-versions/src'),
22
+ 'Http\\Promise\\' => array($vendorDir . '/php-http/promise/src'),
23
+ 'Http\\Message\\' => array($vendorDir . '/php-http/message-factory/src', $vendorDir . '/php-http/message/src'),
24
+ 'Http\\Factory\\Guzzle\\' => array($vendorDir . '/http-interop/http-factory-guzzle/src'),
25
+ 'Http\\Discovery\\' => array($vendorDir . '/php-http/discovery/src'),
26
+ 'Http\\Client\\Common\\' => array($vendorDir . '/php-http/client-common/src'),
27
+ 'Http\\Client\\' => array($vendorDir . '/php-http/httplug/src'),
28
+ 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
29
+ 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
30
+ 'Clue\\StreamFilter\\' => array($vendorDir . '/clue/stream-filter/src'),
31
+ );
src/vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit447dd0acaa396d40f40a0cb37adffd44
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ /**
17
+ * @return \Composer\Autoload\ClassLoader
18
+ */
19
+ public static function getLoader()
20
+ {
21
+ if (null !== self::$loader) {
22
+ return self::$loader;
23
+ }
24
+
25
+ require __DIR__ . '/platform_check.php';
26
+
27
+ spl_autoload_register(array('ComposerAutoloaderInit447dd0acaa396d40f40a0cb37adffd44', 'loadClassLoader'), true, true);
28
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
29
+ spl_autoload_unregister(array('ComposerAutoloaderInit447dd0acaa396d40f40a0cb37adffd44', 'loadClassLoader'));
30
+
31
+ require __DIR__ . '/autoload_static.php';
32
+ call_user_func(\Composer\Autoload\ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44::getInitializer($loader));
33
+
34
+ $loader->register(true);
35
+
36
+ $includeFiles = \Composer\Autoload\ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44::$files;
37
+ foreach ($includeFiles as $fileIdentifier => $file) {
38
+ composerRequire447dd0acaa396d40f40a0cb37adffd44($fileIdentifier, $file);
39
+ }
40
+
41
+ return $loader;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * @param string $fileIdentifier
47
+ * @param string $file
48
+ * @return void
49
+ */
50
+ function composerRequire447dd0acaa396d40f40a0cb37adffd44($fileIdentifier, $file)
51
+ {
52
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
53
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
54
+
55
+ require $file;
56
+ }
57
+ }
src/vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44
8
+ {
9
+ public static $files = array (
10
+ 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
11
+ '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
12
+ '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
13
+ '9c67151ae59aff4788964ce8eb2a0f43' => __DIR__ . '/..' . '/clue/stream-filter/src/functions_include.php',
14
+ '8cff32064859f4559445b89279f3199c' => __DIR__ . '/..' . '/php-http/message/src/filters.php',
15
+ '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
16
+ 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
17
+ '09f6b20656683369174dd6fa83b7e5fb' => __DIR__ . '/..' . '/symfony/polyfill-uuid/bootstrap.php',
18
+ 'fb4ca2d97fe7ba6af750497425204e70' => __DIR__ . '/..' . '/sentry/sentry/src/functions.php',
19
+ );
20
+
21
+ public static $prefixLengthsPsr4 = array (
22
+ 'S' =>
23
+ array (
24
+ 'Symfony\\Polyfill\\Uuid\\' => 22,
25
+ 'Symfony\\Polyfill\\Php80\\' => 23,
26
+ 'Symfony\\Polyfill\\Php73\\' => 23,
27
+ 'Symfony\\Contracts\\Service\\' => 26,
28
+ 'Symfony\\Contracts\\HttpClient\\' => 29,
29
+ 'Symfony\\Component\\OptionsResolver\\' => 34,
30
+ 'Symfony\\Component\\HttpClient\\' => 29,
31
+ 'Sentry\\' => 7,
32
+ ),
33
+ 'P' =>
34
+ array (
35
+ 'Psr\\Log\\' => 8,
36
+ 'Psr\\Http\\Message\\' => 17,
37
+ 'Psr\\Http\\Client\\' => 16,
38
+ 'Psr\\Container\\' => 14,
39
+ ),
40
+ 'J' =>
41
+ array (
42
+ 'Jean85\\' => 7,
43
+ ),
44
+ 'H' =>
45
+ array (
46
+ 'Http\\Promise\\' => 13,
47
+ 'Http\\Message\\' => 13,
48
+ 'Http\\Factory\\Guzzle\\' => 20,
49
+ 'Http\\Discovery\\' => 15,
50
+ 'Http\\Client\\Common\\' => 19,
51
+ 'Http\\Client\\' => 12,
52
+ ),
53
+ 'G' =>
54
+ array (
55
+ 'GuzzleHttp\\Psr7\\' => 16,
56
+ 'GuzzleHttp\\Promise\\' => 19,
57
+ ),
58
+ 'C' =>
59
+ array (
60
+ 'Clue\\StreamFilter\\' => 18,
61
+ ),
62
+ );
63
+
64
+ public static $prefixDirsPsr4 = array (
65
+ 'Symfony\\Polyfill\\Uuid\\' =>
66
+ array (
67
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-uuid',
68
+ ),
69
+ 'Symfony\\Polyfill\\Php80\\' =>
70
+ array (
71
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
72
+ ),
73
+ 'Symfony\\Polyfill\\Php73\\' =>
74
+ array (
75
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-php73',
76
+ ),
77
+ 'Symfony\\Contracts\\Service\\' =>
78
+ array (
79
+ 0 => __DIR__ . '/..' . '/symfony/service-contracts',
80
+ ),
81
+ 'Symfony\\Contracts\\HttpClient\\' =>
82
+ array (
83
+ 0 => __DIR__ . '/..' . '/symfony/http-client-contracts',
84
+ ),
85
+ 'Symfony\\Component\\OptionsResolver\\' =>
86
+ array (
87
+ 0 => __DIR__ . '/..' . '/symfony/options-resolver',
88
+ ),
89
+ 'Symfony\\Component\\HttpClient\\' =>
90
+ array (
91
+ 0 => __DIR__ . '/..' . '/symfony/http-client',
92
+ ),
93
+ 'Sentry\\' =>
94
+ array (
95
+ 0 => __DIR__ . '/..' . '/sentry/sentry/src',
96
+ ),
97
+ 'Psr\\Log\\' =>
98
+ array (
99
+ 0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
100
+ ),
101
+ 'Psr\\Http\\Message\\' =>
102
+ array (
103
+ 0 => __DIR__ . '/..' . '/psr/http-message/src',
104
+ 1 => __DIR__ . '/..' . '/psr/http-factory/src',
105
+ ),
106
+ 'Psr\\Http\\Client\\' =>
107
+ array (
108
+ 0 => __DIR__ . '/..' . '/psr/http-client/src',
109
+ ),
110
+ 'Psr\\Container\\' =>
111
+ array (
112
+ 0 => __DIR__ . '/..' . '/psr/container/src',
113
+ ),
114
+ 'Jean85\\' =>
115
+ array (
116
+ 0 => __DIR__ . '/..' . '/jean85/pretty-package-versions/src',
117
+ ),
118
+ 'Http\\Promise\\' =>
119
+ array (
120
+ 0 => __DIR__ . '/..' . '/php-http/promise/src',
121
+ ),
122
+ 'Http\\Message\\' =>
123
+ array (
124
+ 0 => __DIR__ . '/..' . '/php-http/message-factory/src',
125
+ 1 => __DIR__ . '/..' . '/php-http/message/src',
126
+ ),
127
+ 'Http\\Factory\\Guzzle\\' =>
128
+ array (
129
+ 0 => __DIR__ . '/..' . '/http-interop/http-factory-guzzle/src',
130
+ ),
131
+ 'Http\\Discovery\\' =>
132
+ array (
133
+ 0 => __DIR__ . '/..' . '/php-http/discovery/src',
134
+ ),
135
+ 'Http\\Client\\Common\\' =>
136
+ array (
137
+ 0 => __DIR__ . '/..' . '/php-http/client-common/src',
138
+ ),
139
+ 'Http\\Client\\' =>
140
+ array (
141
+ 0 => __DIR__ . '/..' . '/php-http/httplug/src',
142
+ ),
143
+ 'GuzzleHttp\\Psr7\\' =>
144
+ array (
145
+ 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
146
+ ),
147
+ 'GuzzleHttp\\Promise\\' =>
148
+ array (
149
+ 0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
150
+ ),
151
+ 'Clue\\StreamFilter\\' =>
152
+ array (
153
+ 0 => __DIR__ . '/..' . '/clue/stream-filter/src',
154
+ ),
155
+ );
156
+
157
+ public static $classMap = array (
158
+ 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
159
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
160
+ 'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
161
+ 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
162
+ 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
163
+ 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
164
+ 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
165
+ );
166
+
167
+ public static function getInitializer(ClassLoader $loader)
168
+ {
169
+ return \Closure::bind(function () use ($loader) {
170
+ $loader->prefixLengthsPsr4 = ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44::$prefixLengthsPsr4;
171
+ $loader->prefixDirsPsr4 = ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44::$prefixDirsPsr4;
172
+ $loader->classMap = ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44::$classMap;
173
+
174
+ }, null, ClassLoader::class);
175
+ }
176
+ }
src/vendor/composer/installed.json ADDED
@@ -0,0 +1,1949 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "packages": [
3
+ {
4
+ "name": "clue/stream-filter",
5
+ "version": "v1.6.0",
6
+ "version_normalized": "1.6.0.0",
7
+ "source": {
8
+ "type": "git",
9
+ "url": "https://github.com/clue/stream-filter.git",
10
+ "reference": "d6169430c7731d8509da7aecd0af756a5747b78e"
11
+ },
12
+ "dist": {
13
+ "type": "zip",
14
+ "url": "https://api.github.com/repos/clue/stream-filter/zipball/d6169430c7731d8509da7aecd0af756a5747b78e",
15
+ "reference": "d6169430c7731d8509da7aecd0af756a5747b78e",
16
+ "shasum": ""
17
+ },
18
+ "require": {
19
+ "php": ">=5.3"
20
+ },
21
+ "require-dev": {
22
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36"
23
+ },
24
+ "time": "2022-02-21T13:15:14+00:00",
25
+ "type": "library",
26
+ "installation-source": "dist",
27
+ "autoload": {
28
+ "files": [
29
+ "src/functions_include.php"
30
+ ],
31
+ "psr-4": {
32
+ "Clue\\StreamFilter\\": "src/"
33
+ }
34
+ },
35
+ "notification-url": "https://packagist.org/downloads/",
36
+ "license": [
37
+ "MIT"
38
+ ],
39
+ "authors": [
40
+ {
41
+ "name": "Christian Lück",
42
+ "email": "christian@clue.engineering"
43
+ }
44
+ ],
45
+ "description": "A simple and modern approach to stream filtering in PHP",
46
+ "homepage": "https://github.com/clue/php-stream-filter",
47
+ "keywords": [
48
+ "bucket brigade",
49
+ "callback",
50
+ "filter",
51
+ "php_user_filter",
52
+ "stream",
53
+ "stream_filter_append",
54
+ "stream_filter_register"
55
+ ],
56
+ "support": {
57
+ "issues": "https://github.com/clue/stream-filter/issues",
58
+ "source": "https://github.com/clue/stream-filter/tree/v1.6.0"
59
+ },
60
+ "funding": [
61
+ {
62
+ "url": "https://clue.engineering/support",
63
+ "type": "custom"
64
+ },
65
+ {
66
+ "url": "https://github.com/clue",
67
+ "type": "github"
68
+ }
69
+ ],
70
+ "install-path": "../clue/stream-filter"
71
+ },
72
+ {
73
+ "name": "guzzlehttp/promises",
74
+ "version": "1.5.1",
75
+ "version_normalized": "1.5.1.0",
76
+ "source": {
77
+ "type": "git",
78
+ "url": "https://github.com/guzzle/promises.git",
79
+ "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
80
+ },
81
+ "dist": {
82
+ "type": "zip",
83
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
84
+ "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
85
+ "shasum": ""
86
+ },
87
+ "require": {
88
+ "php": ">=5.5"
89
+ },
90
+ "require-dev": {
91
+ "symfony/phpunit-bridge": "^4.4 || ^5.1"
92
+ },
93
+ "time": "2021-10-22T20:56:57+00:00",
94
+ "type": "library",
95
+ "extra": {
96
+ "branch-alias": {
97
+ "dev-master": "1.5-dev"
98
+ }
99
+ },
100
+ "installation-source": "dist",
101
+ "autoload": {
102
+ "files": [
103
+ "src/functions_include.php"
104
+ ],
105
+ "psr-4": {
106
+ "GuzzleHttp\\Promise\\": "src/"
107
+ }
108
+ },
109
+ "notification-url": "https://packagist.org/downloads/",
110
+ "license": [
111
+ "MIT"
112
+ ],
113
+ "authors": [
114
+ {
115
+ "name": "Graham Campbell",
116
+ "email": "hello@gjcampbell.co.uk",
117
+ "homepage": "https://github.com/GrahamCampbell"
118
+ },
119
+ {
120
+ "name": "Michael Dowling",
121
+ "email": "mtdowling@gmail.com",
122
+ "homepage": "https://github.com/mtdowling"
123
+ },
124
+ {
125
+ "name": "Tobias Nyholm",
126
+ "email": "tobias.nyholm@gmail.com",
127
+ "homepage": "https://github.com/Nyholm"
128
+ },
129
+ {
130
+ "name": "Tobias Schultze",
131
+ "email": "webmaster@tubo-world.de",
132
+ "homepage": "https://github.com/Tobion"
133
+ }
134
+ ],
135
+ "description": "Guzzle promises library",
136
+ "keywords": [
137
+ "promise"
138
+ ],
139
+ "support": {
140
+ "issues": "https://github.com/guzzle/promises/issues",
141
+ "source": "https://github.com/guzzle/promises/tree/1.5.1"
142
+ },
143
+ "funding": [
144
+ {
145
+ "url": "https://github.com/GrahamCampbell",
146
+ "type": "github"
147
+ },
148
+ {
149
+ "url": "https://github.com/Nyholm",
150
+ "type": "github"
151
+ },
152
+ {
153
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
154
+ "type": "tidelift"
155
+ }
156
+ ],
157
+ "install-path": "../guzzlehttp/promises"
158
+ },
159
+ {
160
+ "name": "guzzlehttp/psr7",
161
+ "version": "2.3.0",
162
+ "version_normalized": "2.3.0.0",
163
+ "source": {
164
+ "type": "git",
165
+ "url": "https://github.com/guzzle/psr7.git",
166
+ "reference": "83260bb50b8fc753c72d14dc1621a2dac31877ee"
167
+ },
168
+ "dist": {
169
+ "type": "zip",
170
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/83260bb50b8fc753c72d14dc1621a2dac31877ee",
171
+ "reference": "83260bb50b8fc753c72d14dc1621a2dac31877ee",
172
+ "shasum": ""
173
+ },
174
+ "require": {
175
+ "php": "^7.2.5 || ^8.0",
176
+ "psr/http-factory": "^1.0",
177
+ "psr/http-message": "^1.0",
178
+ "ralouphie/getallheaders": "^3.0"
179
+ },
180
+ "provide": {
181
+ "psr/http-factory-implementation": "1.0",
182
+ "psr/http-message-implementation": "1.0"
183
+ },
184
+ "require-dev": {
185
+ "bamarni/composer-bin-plugin": "^1.4.1",
186
+ "http-interop/http-factory-tests": "^0.9",
187
+ "phpunit/phpunit": "^8.5.8 || ^9.3.10"
188
+ },
189
+ "suggest": {
190
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
191
+ },
192
+ "time": "2022-06-09T08:26:02+00:00",
193
+ "type": "library",
194
+ "extra": {
195
+ "branch-alias": {
196
+ "dev-master": "2.3-dev"
197
+ }
198
+ },
199
+ "installation-source": "dist",
200
+ "autoload": {
201
+ "psr-4": {
202
+ "GuzzleHttp\\Psr7\\": "src/"
203
+ }
204
+ },
205
+ "notification-url": "https://packagist.org/downloads/",
206
+ "license": [
207
+ "MIT"
208
+ ],
209
+ "authors": [
210
+ {
211
+ "name": "Graham Campbell",
212
+ "email": "hello@gjcampbell.co.uk",
213
+ "homepage": "https://github.com/GrahamCampbell"
214
+ },
215
+ {
216
+ "name": "Michael Dowling",
217
+ "email": "mtdowling@gmail.com",
218
+ "homepage": "https://github.com/mtdowling"
219
+ },
220
+ {
221
+ "name": "George Mponos",
222
+ "email": "gmponos@gmail.com",
223
+ "homepage": "https://github.com/gmponos"
224
+ },
225
+ {
226
+ "name": "Tobias Nyholm",
227
+ "email": "tobias.nyholm@gmail.com",
228
+ "homepage": "https://github.com/Nyholm"
229
+ },
230
+ {
231
+ "name": "Márk Sági-Kazár",
232
+ "email": "mark.sagikazar@gmail.com",
233
+ "homepage": "https://github.com/sagikazarmark"
234
+ },
235
+ {
236
+ "name": "Tobias Schultze",
237
+ "email": "webmaster@tubo-world.de",
238
+ "homepage": "https://github.com/Tobion"
239
+ },
240
+ {
241
+ "name": "Márk Sági-Kazár",
242
+ "email": "mark.sagikazar@gmail.com",
243
+ "homepage": "https://sagikazarmark.hu"
244
+ }
245
+ ],
246
+ "description": "PSR-7 message implementation that also provides common utility methods",
247
+ "keywords": [
248
+ "http",
249
+ "message",
250
+ "psr-7",
251
+ "request",
252
+ "response",
253
+ "stream",
254
+ "uri",
255
+ "url"
256
+ ],
257
+ "support": {
258
+ "issues": "https://github.com/guzzle/psr7/issues",
259
+ "source": "https://github.com/guzzle/psr7/tree/2.3.0"
260
+ },
261
+ "funding": [
262
+ {
263
+ "url": "https://github.com/GrahamCampbell",
264
+ "type": "github"
265
+ },
266
+ {
267
+ "url": "https://github.com/Nyholm",
268
+ "type": "github"
269
+ },
270
+ {
271
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
272
+ "type": "tidelift"
273
+ }
274
+ ],
275
+ "install-path": "../guzzlehttp/psr7"
276
+ },
277
+ {
278
+ "name": "http-interop/http-factory-guzzle",
279
+ "version": "1.2.0",
280
+ "version_normalized": "1.2.0.0",
281
+ "source": {
282
+ "type": "git",
283
+ "url": "https://github.com/http-interop/http-factory-guzzle.git",
284
+ "reference": "8f06e92b95405216b237521cc64c804dd44c4a81"
285
+ },
286
+ "dist": {
287
+ "type": "zip",
288
+ "url": "https://api.github.com/repos/http-interop/http-factory-guzzle/zipball/8f06e92b95405216b237521cc64c804dd44c4a81",
289
+ "reference": "8f06e92b95405216b237521cc64c804dd44c4a81",
290
+ "shasum": ""
291
+ },
292
+ "require": {
293
+ "guzzlehttp/psr7": "^1.7||^2.0",
294
+ "php": ">=7.3",
295
+ "psr/http-factory": "^1.0"
296
+ },
297
+ "provide": {
298
+ "psr/http-factory-implementation": "^1.0"
299
+ },
300
+ "require-dev": {
301
+ "http-interop/http-factory-tests": "^0.9",
302
+ "phpunit/phpunit": "^9.5"
303
+ },
304
+ "suggest": {
305
+ "guzzlehttp/psr7": "Includes an HTTP factory starting in version 2.0"
306
+ },
307
+ "time": "2021-07-21T13:50:14+00:00",
308
+ "type": "library",
309
+ "installation-source": "dist",
310
+ "autoload": {
311
+ "psr-4": {
312
+ "Http\\Factory\\Guzzle\\": "src/"
313
+ }
314
+ },
315
+ "notification-url": "https://packagist.org/downloads/",
316
+ "license": [
317
+ "MIT"
318
+ ],
319
+ "authors": [
320
+ {
321
+ "name": "PHP-FIG",
322
+ "homepage": "http://www.php-fig.org/"
323
+ }
324
+ ],
325
+ "description": "An HTTP Factory using Guzzle PSR7",
326
+ "keywords": [
327
+ "factory",
328
+ "http",
329
+ "psr-17",
330
+ "psr-7"
331
+ ],
332
+ "support": {
333
+ "issues": "https://github.com/http-interop/http-factory-guzzle/issues",
334
+ "source": "https://github.com/http-interop/http-factory-guzzle/tree/1.2.0"
335
+ },
336
+ "install-path": "../http-interop/http-factory-guzzle"
337
+ },
338
+ {
339
+ "name": "jean85/pretty-package-versions",
340
+ "version": "2.0.5",
341
+ "version_normalized": "2.0.5.0",
342
+ "source": {
343
+ "type": "git",
344
+ "url": "https://github.com/Jean85/pretty-package-versions.git",
345
+ "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af"
346
+ },
347
+ "dist": {
348
+ "type": "zip",
349
+ "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af",
350
+ "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af",
351
+ "shasum": ""
352
+ },
353
+ "require": {
354
+ "composer-runtime-api": "^2.0.0",
355
+ "php": "^7.1|^8.0"
356
+ },
357
+ "require-dev": {
358
+ "friendsofphp/php-cs-fixer": "^2.17",
359
+ "jean85/composer-provided-replaced-stub-package": "^1.0",
360
+ "phpstan/phpstan": "^0.12.66",
361
+ "phpunit/phpunit": "^7.5|^8.5|^9.4",
362
+ "vimeo/psalm": "^4.3"
363
+ },
364
+ "time": "2021-10-08T21:21:46+00:00",
365
+ "type": "library",
366
+ "extra": {
367
+ "branch-alias": {
368
+ "dev-master": "1.x-dev"
369
+ }
370
+ },
371
+ "installation-source": "dist",
372
+ "autoload": {
373
+ "psr-4": {
374
+ "Jean85\\": "src/"
375
+ }
376
+ },
377
+ "notification-url": "https://packagist.org/downloads/",
378
+ "license": [
379
+ "MIT"
380
+ ],
381
+ "authors": [
382
+ {
383
+ "name": "Alessandro Lai",
384
+ "email": "alessandro.lai85@gmail.com"
385
+ }
386
+ ],
387
+ "description": "A library to get pretty versions strings of installed dependencies",
388
+ "keywords": [
389
+ "composer",
390
+ "package",
391
+ "release",
392
+ "versions"
393
+ ],
394
+ "support": {
395
+ "issues": "https://github.com/Jean85/pretty-package-versions/issues",
396
+ "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5"
397
+ },
398
+ "install-path": "../jean85/pretty-package-versions"
399
+ },
400
+ {
401
+ "name": "php-http/client-common",
402
+ "version": "2.5.0",
403
+ "version_normalized": "2.5.0.0",
404
+ "source": {
405
+ "type": "git",
406
+ "url": "https://github.com/php-http/client-common.git",
407
+ "reference": "d135751167d57e27c74de674d6a30cef2dc8e054"
408
+ },
409
+ "dist": {
410
+ "type": "zip",
411
+ "url": "https://api.github.com/repos/php-http/client-common/zipball/d135751167d57e27c74de674d6a30cef2dc8e054",
412
+ "reference": "d135751167d57e27c74de674d6a30cef2dc8e054",
413
+ "shasum": ""
414
+ },
415
+ "require": {
416
+ "php": "^7.1 || ^8.0",
417
+ "php-http/httplug": "^2.0",
418
+ "php-http/message": "^1.6",
419
+ "php-http/message-factory": "^1.0",
420
+ "psr/http-client": "^1.0",
421
+ "psr/http-factory": "^1.0",
422
+ "psr/http-message": "^1.0",
423
+ "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0",
424
+ "symfony/polyfill-php80": "^1.17"
425
+ },
426
+ "require-dev": {
427
+ "doctrine/instantiator": "^1.1",
428
+ "guzzlehttp/psr7": "^1.4",
429
+ "nyholm/psr7": "^1.2",
430
+ "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
431
+ "phpspec/prophecy": "^1.10.2",
432
+ "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3"
433
+ },
434
+ "suggest": {
435
+ "ext-json": "To detect JSON responses with the ContentTypePlugin",
436
+ "ext-libxml": "To detect XML responses with the ContentTypePlugin",
437
+ "php-http/cache-plugin": "PSR-6 Cache plugin",
438
+ "php-http/logger-plugin": "PSR-3 Logger plugin",
439
+ "php-http/stopwatch-plugin": "Symfony Stopwatch plugin"
440
+ },
441
+ "time": "2021-11-26T15:01:24+00:00",
442
+ "type": "library",
443
+ "extra": {
444
+ "branch-alias": {
445
+ "dev-master": "2.3.x-dev"
446
+ }
447
+ },
448
+ "installation-source": "dist",
449
+ "autoload": {
450
+ "psr-4": {
451
+ "Http\\Client\\Common\\": "src/"
452
+ }
453
+ },
454
+ "notification-url": "https://packagist.org/downloads/",
455
+ "license": [
456
+ "MIT"
457
+ ],
458
+ "authors": [
459
+ {
460
+ "name": "Márk Sági-Kazár",
461
+ "email": "mark.sagikazar@gmail.com"
462
+ }
463
+ ],
464
+ "description": "Common HTTP Client implementations and tools for HTTPlug",
465
+ "homepage": "http://httplug.io",
466
+ "keywords": [
467
+ "client",
468
+ "common",
469
+ "http",
470
+ "httplug"
471
+ ],
472
+ "support": {
473
+ "issues": "https://github.com/php-http/client-common/issues",
474
+ "source": "https://github.com/php-http/client-common/tree/2.5.0"
475
+ },
476
+ "install-path": "../php-http/client-common"
477
+ },
478
+ {
479
+ "name": "php-http/discovery",
480
+ "version": "1.14.2",
481
+ "version_normalized": "1.14.2.0",
482
+ "source": {
483
+ "type": "git",
484
+ "url": "https://github.com/php-http/discovery.git",
485
+ "reference": "c8d48852fbc052454af42f6de27635ddd916b959"
486
+ },
487
+ "dist": {
488
+ "type": "zip",
489
+ "url": "https://api.github.com/repos/php-http/discovery/zipball/c8d48852fbc052454af42f6de27635ddd916b959",
490
+ "reference": "c8d48852fbc052454af42f6de27635ddd916b959",
491
+ "shasum": ""
492
+ },
493
+ "require": {
494
+ "php": "^7.1 || ^8.0"
495
+ },
496
+ "conflict": {
497
+ "nyholm/psr7": "<1.0"
498
+ },
499
+ "require-dev": {
500
+ "graham-campbell/phpspec-skip-example-extension": "^5.0",
501
+ "php-http/httplug": "^1.0 || ^2.0",
502
+ "php-http/message-factory": "^1.0",
503
+ "phpspec/phpspec": "^5.1 || ^6.1"
504
+ },
505
+ "suggest": {
506
+ "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories"
507
+ },
508
+ "time": "2022-05-25T07:26:05+00:00",
509
+ "type": "library",
510
+ "extra": {
511
+ "branch-alias": {
512
+ "dev-master": "1.9-dev"
513
+ }
514
+ },
515
+ "installation-source": "dist",
516
+ "autoload": {
517
+ "psr-4": {
518
+ "Http\\Discovery\\": "src/"
519
+ }
520
+ },
521
+ "notification-url": "https://packagist.org/downloads/",
522
+ "license": [
523
+ "MIT"
524
+ ],
525
+ "authors": [
526
+ {
527
+ "name": "Márk Sági-Kazár",
528
+ "email": "mark.sagikazar@gmail.com"
529
+ }
530
+ ],
531
+ "description": "Finds installed HTTPlug implementations and PSR-7 message factories",
532
+ "homepage": "http://php-http.org",
533
+ "keywords": [
534
+ "adapter",
535
+ "client",
536
+ "discovery",
537
+ "factory",
538
+ "http",
539
+ "message",
540
+ "psr7"
541
+ ],
542
+ "support": {
543
+ "issues": "https://github.com/php-http/discovery/issues",
544
+ "source": "https://github.com/php-http/discovery/tree/1.14.2"
545
+ },
546
+ "install-path": "../php-http/discovery"
547
+ },
548
+ {
549
+ "name": "php-http/httplug",
550
+ "version": "2.3.0",
551
+ "version_normalized": "2.3.0.0",
552
+ "source": {
553
+ "type": "git",
554
+ "url": "https://github.com/php-http/httplug.git",
555
+ "reference": "f640739f80dfa1152533976e3c112477f69274eb"
556
+ },
557
+ "dist": {
558
+ "type": "zip",
559
+ "url": "https://api.github.com/repos/php-http/httplug/zipball/f640739f80dfa1152533976e3c112477f69274eb",
560
+ "reference": "f640739f80dfa1152533976e3c112477f69274eb",
561
+ "shasum": ""
562
+ },
563
+ "require": {
564
+ "php": "^7.1 || ^8.0",
565
+ "php-http/promise": "^1.1",
566
+ "psr/http-client": "^1.0",
567
+ "psr/http-message": "^1.0"
568
+ },
569
+ "require-dev": {
570
+ "friends-of-phpspec/phpspec-code-coverage": "^4.1",
571
+ "phpspec/phpspec": "^5.1 || ^6.0"
572
+ },
573
+ "time": "2022-02-21T09:52:22+00:00",
574
+ "type": "library",
575
+ "extra": {
576
+ "branch-alias": {
577
+ "dev-master": "2.x-dev"
578
+ }
579
+ },
580
+ "installation-source": "dist",
581
+ "autoload": {
582
+ "psr-4": {
583
+ "Http\\Client\\": "src/"
584
+ }
585
+ },
586
+ "notification-url": "https://packagist.org/downloads/",
587
+ "license": [
588
+ "MIT"
589
+ ],
590
+ "authors": [
591
+ {
592
+ "name": "Eric GELOEN",
593
+ "email": "geloen.eric@gmail.com"
594
+ },
595
+ {
596
+ "name": "Márk Sági-Kazár",
597
+ "email": "mark.sagikazar@gmail.com",
598
+ "homepage": "https://sagikazarmark.hu"
599
+ }
600
+ ],
601
+ "description": "HTTPlug, the HTTP client abstraction for PHP",
602
+ "homepage": "http://httplug.io",
603
+ "keywords": [
604
+ "client",
605
+ "http"
606
+ ],
607
+ "support": {
608
+ "issues": "https://github.com/php-http/httplug/issues",
609
+ "source": "https://github.com/php-http/httplug/tree/2.3.0"
610
+ },
611
+ "install-path": "../php-http/httplug"
612
+ },
613
+ {
614
+ "name": "php-http/message",
615
+ "version": "1.13.0",
616
+ "version_normalized": "1.13.0.0",
617
+ "source": {
618
+ "type": "git",
619
+ "url": "https://github.com/php-http/message.git",
620
+ "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361"
621
+ },
622
+ "dist": {
623
+ "type": "zip",
624
+ "url": "https://api.github.com/repos/php-http/message/zipball/7886e647a30a966a1a8d1dad1845b71ca8678361",
625
+ "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361",
626
+ "shasum": ""
627
+ },
628
+ "require": {
629
+ "clue/stream-filter": "^1.5",
630
+ "php": "^7.1 || ^8.0",
631
+ "php-http/message-factory": "^1.0.2",
632
+ "psr/http-message": "^1.0"
633
+ },
634
+ "provide": {
635
+ "php-http/message-factory-implementation": "1.0"
636
+ },
637
+ "require-dev": {
638
+ "ergebnis/composer-normalize": "^2.6",
639
+ "ext-zlib": "*",
640
+ "guzzlehttp/psr7": "^1.0",
641
+ "laminas/laminas-diactoros": "^2.0",
642
+ "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
643
+ "slim/slim": "^3.0"
644
+ },
645
+ "suggest": {
646
+ "ext-zlib": "Used with compressor/decompressor streams",
647
+ "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories",
648
+ "laminas/laminas-diactoros": "Used with Diactoros Factories",
649
+ "slim/slim": "Used with Slim Framework PSR-7 implementation"
650
+ },
651
+ "time": "2022-02-11T13:41:14+00:00",
652
+ "type": "library",
653
+ "extra": {
654
+ "branch-alias": {
655
+ "dev-master": "1.10-dev"
656
+ }
657
+ },
658
+ "installation-source": "dist",
659
+ "autoload": {
660
+ "files": [
661
+ "src/filters.php"
662
+ ],
663
+ "psr-4": {
664
+ "Http\\Message\\": "src/"
665
+ }
666
+ },
667
+ "notification-url": "https://packagist.org/downloads/",
668
+ "license": [
669
+ "MIT"
670
+ ],
671
+ "authors": [
672
+ {
673
+ "name": "Márk Sági-Kazár",
674
+ "email": "mark.sagikazar@gmail.com"
675
+ }
676
+ ],
677
+ "description": "HTTP Message related tools",
678
+ "homepage": "http://php-http.org",
679
+ "keywords": [
680
+ "http",
681
+ "message",
682
+ "psr-7"
683
+ ],
684
+ "support": {
685
+ "issues": "https://github.com/php-http/message/issues",
686
+ "source": "https://github.com/php-http/message/tree/1.13.0"
687
+ },
688
+ "install-path": "../php-http/message"
689
+ },
690
+ {
691
+ "name": "php-http/message-factory",
692
+ "version": "v1.0.2",
693
+ "version_normalized": "1.0.2.0",
694
+ "source": {
695
+ "type": "git",
696
+ "url": "https://github.com/php-http/message-factory.git",
697
+ "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1"
698
+ },
699
+ "dist": {
700
+ "type": "zip",
701
+ "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1",
702
+ "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1",
703
+ "shasum": ""
704
+ },
705
+ "require": {
706
+ "php": ">=5.4",
707
+ "psr/http-message": "^1.0"
708
+ },
709
+ "time": "2015-12-19T14:08:53+00:00",
710
+ "type": "library",
711
+ "extra": {
712
+ "branch-alias": {
713
+ "dev-master": "1.0-dev"
714
+ }
715
+ },
716
+ "installation-source": "dist",
717
+ "autoload": {
718
+ "psr-4": {
719
+ "Http\\Message\\": "src/"
720
+ }
721
+ },
722
+ "notification-url": "https://packagist.org/downloads/",
723
+ "license": [
724
+ "MIT"
725
+ ],
726
+ "authors": [
727
+ {
728
+ "name": "Márk Sági-Kazár",
729
+ "email": "mark.sagikazar@gmail.com"
730
+ }
731
+ ],
732
+ "description": "Factory interfaces for PSR-7 HTTP Message",
733
+ "homepage": "http://php-http.org",
734
+ "keywords": [
735
+ "factory",
736
+ "http",
737
+ "message",
738
+ "stream",
739
+ "uri"
740
+ ],
741
+ "support": {
742
+ "issues": "https://github.com/php-http/message-factory/issues",
743
+ "source": "https://github.com/php-http/message-factory/tree/master"
744
+ },
745
+ "install-path": "../php-http/message-factory"
746
+ },
747
+ {
748
+ "name": "php-http/promise",
749
+ "version": "1.1.0",
750
+ "version_normalized": "1.1.0.0",
751
+ "source": {
752
+ "type": "git",
753
+ "url": "https://github.com/php-http/promise.git",
754
+ "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88"
755
+ },
756
+ "dist": {
757
+ "type": "zip",
758
+ "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
759
+ "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88",
760
+ "shasum": ""
761
+ },
762
+ "require": {
763
+ "php": "^7.1 || ^8.0"
764
+ },
765
+ "require-dev": {
766
+ "friends-of-phpspec/phpspec-code-coverage": "^4.3.2",
767
+ "phpspec/phpspec": "^5.1.2 || ^6.2"
768
+ },
769
+ "time": "2020-07-07T09:29:14+00:00",
770
+ "type": "library",
771
+ "extra": {
772
+ "branch-alias": {
773
+ "dev-master": "1.1-dev"
774
+ }
775
+ },
776
+ "installation-source": "dist",
777
+ "autoload": {
778
+ "psr-4": {
779
+ "Http\\Promise\\": "src/"
780
+ }
781
+ },
782
+ "notification-url": "https://packagist.org/downloads/",
783
+ "license": [
784
+ "MIT"
785
+ ],
786
+ "authors": [
787
+ {
788
+ "name": "Joel Wurtz",
789
+ "email": "joel.wurtz@gmail.com"
790
+ },
791
+ {
792
+ "name": "Márk Sági-Kazár",
793
+ "email": "mark.sagikazar@gmail.com"
794
+ }
795
+ ],
796
+ "description": "Promise used for asynchronous HTTP requests",
797
+ "homepage": "http://httplug.io",
798
+ "keywords": [
799
+ "promise"
800
+ ],
801
+ "support": {
802
+ "issues": "https://github.com/php-http/promise/issues",
803
+ "source": "https://github.com/php-http/promise/tree/1.1.0"
804
+ },
805
+ "install-path": "../php-http/promise"
806
+ },
807
+ {
808
+ "name": "psr/container",
809
+ "version": "1.1.2",
810
+ "version_normalized": "1.1.2.0",
811
+ "source": {
812
+ "type": "git",
813
+ "url": "https://github.com/php-fig/container.git",
814
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
815
+ },
816
+ "dist": {
817
+ "type": "zip",
818
+ "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
819
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
820
+ "shasum": ""
821
+ },
822
+ "require": {
823
+ "php": ">=7.4.0"
824
+ },
825
+ "time": "2021-11-05T16:50:12+00:00",
826
+ "type": "library",
827
+ "installation-source": "dist",
828
+ "autoload": {
829
+ "psr-4": {
830
+ "Psr\\Container\\": "src/"
831
+ }
832
+ },
833
+ "notification-url": "https://packagist.org/downloads/",
834
+ "license": [
835
+ "MIT"
836
+ ],
837
+ "authors": [
838
+ {
839
+ "name": "PHP-FIG",
840
+ "homepage": "https://www.php-fig.org/"
841
+ }
842
+ ],
843
+ "description": "Common Container Interface (PHP FIG PSR-11)",
844
+ "homepage": "https://github.com/php-fig/container",
845
+ "keywords": [
846
+ "PSR-11",
847
+ "container",
848
+ "container-interface",
849
+ "container-interop",
850
+ "psr"
851
+ ],
852
+ "support": {
853
+ "issues": "https://github.com/php-fig/container/issues",
854
+ "source": "https://github.com/php-fig/container/tree/1.1.2"
855
+ },
856
+ "install-path": "../psr/container"
857
+ },
858
+ {
859
+ "name": "psr/http-client",
860
+ "version": "1.0.1",
861
+ "version_normalized": "1.0.1.0",
862
+ "source": {
863
+ "type": "git",
864
+ "url": "https://github.com/php-fig/http-client.git",
865
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
866
+ },
867
+ "dist": {
868
+ "type": "zip",
869
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
870
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
871
+ "shasum": ""
872
+ },
873
+ "require": {
874
+ "php": "^7.0 || ^8.0",
875
+ "psr/http-message": "^1.0"
876
+ },
877
+ "time": "2020-06-29T06:28:15+00:00",
878
+ "type": "library",
879
+ "extra": {
880
+ "branch-alias": {
881
+ "dev-master": "1.0.x-dev"
882
+ }
883
+ },
884
+ "installation-source": "dist",
885
+ "autoload": {
886
+ "psr-4": {
887
+ "Psr\\Http\\Client\\": "src/"
888
+ }
889
+ },
890
+ "notification-url": "https://packagist.org/downloads/",
891
+ "license": [
892
+ "MIT"
893
+ ],
894
+ "authors": [
895
+ {
896
+ "name": "PHP-FIG",
897
+ "homepage": "http://www.php-fig.org/"
898
+ }
899
+ ],
900
+ "description": "Common interface for HTTP clients",
901
+ "homepage": "https://github.com/php-fig/http-client",
902
+ "keywords": [
903
+ "http",
904
+ "http-client",
905
+ "psr",
906
+ "psr-18"
907
+ ],
908
+ "support": {
909
+ "source": "https://github.com/php-fig/http-client/tree/master"
910
+ },
911
+ "install-path": "../psr/http-client"
912
+ },
913
+ {
914
+ "name": "psr/http-factory",
915
+ "version": "1.0.1",
916
+ "version_normalized": "1.0.1.0",
917
+ "source": {
918
+ "type": "git",
919
+ "url": "https://github.com/php-fig/http-factory.git",
920
+ "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
921
+ },
922
+ "dist": {
923
+ "type": "zip",
924
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
925
+ "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
926
+ "shasum": ""
927
+ },
928
+ "require": {
929
+ "php": ">=7.0.0",
930
+ "psr/http-message": "^1.0"
931
+ },
932
+ "time": "2019-04-30T12:38:16+00:00",
933
+ "type": "library",
934
+ "extra": {
935
+ "branch-alias": {
936
+ "dev-master": "1.0.x-dev"
937
+ }
938
+ },
939
+ "installation-source": "dist",
940
+ "autoload": {
941
+ "psr-4": {
942
+ "Psr\\Http\\Message\\": "src/"
943
+ }
944
+ },
945
+ "notification-url": "https://packagist.org/downloads/",
946
+ "license": [
947
+ "MIT"
948
+ ],
949
+ "authors": [
950
+ {
951
+ "name": "PHP-FIG",
952
+ "homepage": "http://www.php-fig.org/"
953
+ }
954
+ ],
955
+ "description": "Common interfaces for PSR-7 HTTP message factories",
956
+ "keywords": [
957
+ "factory",
958
+ "http",
959
+ "message",
960
+ "psr",
961
+ "psr-17",
962
+ "psr-7",
963
+ "request",
964
+ "response"
965
+ ],
966
+ "support": {
967
+ "source": "https://github.com/php-fig/http-factory/tree/master"
968
+ },
969
+ "install-path": "../psr/http-factory"
970
+ },
971
+ {
972
+ "name": "psr/http-message",
973
+ "version": "1.0.1",
974
+ "version_normalized": "1.0.1.0",
975
+ "source": {
976
+ "type": "git",
977
+ "url": "https://github.com/php-fig/http-message.git",
978
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
979
+ },
980
+ "dist": {
981
+ "type": "zip",
982
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
983
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
984
+ "shasum": ""
985
+ },
986
+ "require": {
987
+ "php": ">=5.3.0"
988
+ },
989
+ "time": "2016-08-06T14:39:51+00:00",
990
+ "type": "library",
991
+ "extra": {
992
+ "branch-alias": {
993
+ "dev-master": "1.0.x-dev"
994
+ }
995
+ },
996
+ "installation-source": "dist",
997
+ "autoload": {
998
+ "psr-4": {
999
+ "Psr\\Http\\Message\\": "src/"
1000
+ }
1001
+ },
1002
+ "notification-url": "https://packagist.org/downloads/",
1003
+ "license": [
1004
+ "MIT"
1005
+ ],
1006
+ "authors": [
1007
+ {
1008
+ "name": "PHP-FIG",
1009
+ "homepage": "http://www.php-fig.org/"
1010
+ }
1011
+ ],
1012
+ "description": "Common interface for HTTP messages",
1013
+ "homepage": "https://github.com/php-fig/http-message",
1014
+ "keywords": [
1015
+ "http",
1016
+ "http-message",
1017
+ "psr",
1018
+ "psr-7",
1019
+ "request",
1020
+ "response"
1021
+ ],
1022
+ "support": {
1023
+ "source": "https://github.com/php-fig/http-message/tree/master"
1024
+ },
1025
+ "install-path": "../psr/http-message"
1026
+ },
1027
+ {
1028
+ "name": "psr/log",
1029
+ "version": "1.1.4",
1030
+ "version_normalized": "1.1.4.0",
1031
+ "source": {
1032
+ "type": "git",
1033
+ "url": "https://github.com/php-fig/log.git",
1034
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
1035
+ },
1036
+ "dist": {
1037
+ "type": "zip",
1038
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
1039
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
1040
+ "shasum": ""
1041
+ },
1042
+ "require": {
1043
+ "php": ">=5.3.0"
1044
+ },
1045
+ "time": "2021-05-03T11:20:27+00:00",
1046
+ "type": "library",
1047
+ "extra": {
1048
+ "branch-alias": {
1049
+ "dev-master": "1.1.x-dev"
1050
+ }
1051
+ },
1052
+ "installation-source": "dist",
1053
+ "autoload": {
1054
+ "psr-4": {
1055
+ "Psr\\Log\\": "Psr/Log/"
1056
+ }
1057
+ },
1058
+ "notification-url": "https://packagist.org/downloads/",
1059
+ "license": [
1060
+ "MIT"
1061
+ ],
1062
+ "authors": [
1063
+ {
1064
+ "name": "PHP-FIG",
1065
+ "homepage": "https://www.php-fig.org/"
1066
+ }
1067
+ ],
1068
+ "description": "Common interface for logging libraries",
1069
+ "homepage": "https://github.com/php-fig/log",
1070
+ "keywords": [
1071
+ "log",
1072
+ "psr",
1073
+ "psr-3"
1074
+ ],
1075
+ "support": {
1076
+ "source": "https://github.com/php-fig/log/tree/1.1.4"
1077
+ },
1078
+ "install-path": "../psr/log"
1079
+ },
1080
+ {
1081
+ "name": "ralouphie/getallheaders",
1082
+ "version": "3.0.3",
1083
+ "version_normalized": "3.0.3.0",
1084
+ "source": {
1085
+ "type": "git",
1086
+ "url": "https://github.com/ralouphie/getallheaders.git",
1087
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
1088
+ },
1089
+ "dist": {
1090
+ "type": "zip",
1091
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
1092
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
1093
+ "shasum": ""
1094
+ },
1095
+ "require": {
1096
+ "php": ">=5.6"
1097
+ },
1098
+ "require-dev": {
1099
+ "php-coveralls/php-coveralls": "^2.1",
1100
+ "phpunit/phpunit": "^5 || ^6.5"
1101
+ },
1102
+ "time": "2019-03-08T08:55:37+00:00",
1103
+ "type": "library",
1104
+ "installation-source": "dist",
1105
+ "autoload": {
1106
+ "files": [
1107
+ "src/getallheaders.php"
1108
+ ]
1109
+ },
1110
+ "notification-url": "https://packagist.org/downloads/",
1111
+ "license": [
1112
+ "MIT"
1113
+ ],
1114
+ "authors": [
1115
+ {
1116
+ "name": "Ralph Khattar",
1117
+ "email": "ralph.khattar@gmail.com"
1118
+ }
1119
+ ],
1120
+ "description": "A polyfill for getallheaders.",
1121
+ "support": {
1122
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
1123
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
1124
+ },
1125
+ "install-path": "../ralouphie/getallheaders"
1126
+ },
1127
+ {
1128
+ "name": "sentry/sdk",
1129
+ "version": "3.2.0",
1130
+ "version_normalized": "3.2.0.0",
1131
+ "source": {
1132
+ "type": "git",
1133
+ "url": "https://github.com/getsentry/sentry-php-sdk.git",
1134
+ "reference": "6d78bd83b43efbb52f81d6824f4af344fa9ba292"
1135
+ },
1136
+ "dist": {
1137
+ "type": "zip",
1138
+ "url": "https://api.github.com/repos/getsentry/sentry-php-sdk/zipball/6d78bd83b43efbb52f81d6824f4af344fa9ba292",
1139
+ "reference": "6d78bd83b43efbb52f81d6824f4af344fa9ba292",
1140
+ "shasum": ""
1141
+ },
1142
+ "require": {
1143
+ "http-interop/http-factory-guzzle": "^1.0",
1144
+ "sentry/sentry": "^3.5",
1145
+ "symfony/http-client": "^4.3|^5.0|^6.0"
1146
+ },
1147
+ "time": "2022-05-21T11:10:11+00:00",
1148
+ "type": "metapackage",
1149
+ "notification-url": "https://packagist.org/downloads/",
1150
+ "license": [
1151
+ "MIT"
1152
+ ],
1153
+ "authors": [
1154
+ {
1155
+ "name": "Sentry",
1156
+ "email": "accounts@sentry.io"
1157
+ }
1158
+ ],
1159
+ "description": "This is a metapackage shipping sentry/sentry with a recommended HTTP client.",
1160
+ "homepage": "http://sentry.io",
1161
+ "keywords": [
1162
+ "crash-reporting",
1163
+ "crash-reports",
1164
+ "error-handler",
1165
+ "error-monitoring",
1166
+ "log",
1167
+ "logging",
1168
+ "sentry"
1169
+ ],
1170
+ "support": {
1171
+ "issues": "https://github.com/getsentry/sentry-php-sdk/issues",
1172
+ "source": "https://github.com/getsentry/sentry-php-sdk/tree/3.2.0"
1173
+ },
1174
+ "funding": [
1175
+ {
1176
+ "url": "https://sentry.io/",
1177
+ "type": "custom"
1178
+ },
1179
+ {
1180
+ "url": "https://sentry.io/pricing/",
1181
+ "type": "custom"
1182
+ }
1183
+ ],
1184
+ "install-path": null
1185
+ },
1186
+ {
1187
+ "name": "sentry/sentry",
1188
+ "version": "3.6.0",
1189
+ "version_normalized": "3.6.0.0",
1190
+ "source": {
1191
+ "type": "git",
1192
+ "url": "https://github.com/getsentry/sentry-php.git",
1193
+ "reference": "6d1a6ee29c558be373bfe08d454a3c116c02dd0d"
1194
+ },
1195
+ "dist": {
1196
+ "type": "zip",
1197
+ "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/6d1a6ee29c558be373bfe08d454a3c116c02dd0d",
1198
+ "reference": "6d1a6ee29c558be373bfe08d454a3c116c02dd0d",
1199
+ "shasum": ""
1200
+ },
1201
+ "require": {
1202
+ "ext-json": "*",
1203
+ "ext-mbstring": "*",
1204
+ "guzzlehttp/promises": "^1.4",
1205
+ "guzzlehttp/psr7": "^1.8.4|^2.1.1",
1206
+ "jean85/pretty-package-versions": "^1.5|^2.0.4",
1207
+ "php": "^7.2|^8.0",
1208
+ "php-http/async-client-implementation": "^1.0",
1209
+ "php-http/client-common": "^1.5|^2.0",
1210
+ "php-http/discovery": "^1.11",
1211
+ "php-http/httplug": "^1.1|^2.0",
1212
+ "php-http/message": "^1.5",
1213
+ "psr/http-factory": "^1.0",
1214
+ "psr/http-message-implementation": "^1.0",
1215
+ "psr/log": "^1.0|^2.0|^3.0",
1216
+ "symfony/options-resolver": "^3.4.43|^4.4.30|^5.0.11|^6.0",
1217
+ "symfony/polyfill-php80": "^1.17",
1218
+ "symfony/polyfill-uuid": "^1.13.1"
1219
+ },
1220
+ "conflict": {
1221
+ "php-http/client-common": "1.8.0",
1222
+ "raven/raven": "*"
1223
+ },
1224
+ "require-dev": {
1225
+ "friendsofphp/php-cs-fixer": "^2.19|3.4.*",
1226
+ "http-interop/http-factory-guzzle": "^1.0",
1227
+ "monolog/monolog": "^1.6|^2.0|^3.0",
1228
+ "nikic/php-parser": "^4.10.3",
1229
+ "php-http/mock-client": "^1.3",
1230
+ "phpbench/phpbench": "^1.0",
1231
+ "phpstan/extension-installer": "^1.0",
1232
+ "phpstan/phpstan": "^1.3",
1233
+ "phpstan/phpstan-phpunit": "^1.0",
1234
+ "phpunit/phpunit": "^8.5.14|^9.4",
1235
+ "symfony/phpunit-bridge": "^5.2|^6.0",
1236
+ "vimeo/psalm": "^4.17"
1237
+ },
1238
+ "suggest": {
1239
+ "monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler."
1240
+ },
1241
+ "time": "2022-06-09T20:33:39+00:00",
1242
+ "type": "library",
1243
+ "extra": {
1244
+ "branch-alias": {
1245
+ "dev-master": "3.6.x-dev"
1246
+ }
1247
+ },
1248
+ "installation-source": "dist",
1249
+ "autoload": {
1250
+ "files": [
1251
+ "src/functions.php"
1252
+ ],
1253
+ "psr-4": {
1254
+ "Sentry\\": "src/"
1255
+ }
1256
+ },
1257
+ "notification-url": "https://packagist.org/downloads/",
1258
+ "license": [
1259
+ "BSD-3-Clause"
1260
+ ],
1261
+ "authors": [
1262
+ {
1263
+ "name": "Sentry",
1264
+ "email": "accounts@sentry.io"
1265
+ }
1266
+ ],
1267
+ "description": "A PHP SDK for Sentry (http://sentry.io)",
1268
+ "homepage": "http://sentry.io",
1269
+ "keywords": [
1270
+ "crash-reporting",
1271
+ "crash-reports",
1272
+ "error-handler",
1273
+ "error-monitoring",
1274
+ "log",
1275
+ "logging",
1276
+ "sentry"
1277
+ ],
1278
+ "support": {
1279
+ "issues": "https://github.com/getsentry/sentry-php/issues",
1280
+ "source": "https://github.com/getsentry/sentry-php/tree/3.6.0"
1281
+ },
1282
+ "funding": [
1283
+ {
1284
+ "url": "https://sentry.io/",
1285
+ "type": "custom"
1286
+ },
1287
+ {
1288
+ "url": "https://sentry.io/pricing/",
1289
+ "type": "custom"
1290
+ }
1291
+ ],
1292
+ "install-path": "../sentry/sentry"
1293
+ },
1294
+ {
1295
+ "name": "symfony/deprecation-contracts",
1296
+ "version": "v2.5.1",
1297
+ "version_normalized": "2.5.1.0",
1298
+ "source": {
1299
+ "type": "git",
1300
+ "url": "https://github.com/symfony/deprecation-contracts.git",
1301
+ "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
1302
+ },
1303
+ "dist": {
1304
+ "type": "zip",
1305
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
1306
+ "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
1307
+ "shasum": ""
1308
+ },
1309
+ "require": {
1310
+ "php": ">=7.1"
1311
+ },
1312
+ "time": "2022-01-02T09:53:40+00:00",
1313
+ "type": "library",
1314
+ "extra": {
1315
+ "branch-alias": {
1316
+ "dev-main": "2.5-dev"
1317
+ },
1318
+ "thanks": {
1319
+ "name": "symfony/contracts",
1320
+ "url": "https://github.com/symfony/contracts"
1321
+ }
1322
+ },
1323
+ "installation-source": "dist",
1324
+ "autoload": {
1325
+ "files": [
1326
+ "function.php"
1327
+ ]
1328
+ },
1329
+ "notification-url": "https://packagist.org/downloads/",
1330
+ "license": [
1331
+ "MIT"
1332
+ ],
1333
+ "authors": [
1334
+ {
1335
+ "name": "Nicolas Grekas",
1336
+ "email": "p@tchwork.com"
1337
+ },
1338
+ {
1339
+ "name": "Symfony Community",
1340
+ "homepage": "https://symfony.com/contributors"
1341
+ }
1342
+ ],
1343
+ "description": "A generic function and convention to trigger deprecation notices",
1344
+ "homepage": "https://symfony.com",
1345
+ "support": {
1346
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1"
1347
+ },
1348
+ "funding": [
1349
+ {
1350
+ "url": "https://symfony.com/sponsor",
1351
+ "type": "custom"
1352
+ },
1353
+ {
1354
+ "url": "https://github.com/fabpot",
1355
+ "type": "github"
1356
+ },
1357
+ {
1358
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1359
+ "type": "tidelift"
1360
+ }
1361
+ ],
1362
+ "install-path": "../symfony/deprecation-contracts"
1363
+ },
1364
+ {
1365
+ "name": "symfony/http-client",
1366
+ "version": "v5.4.9",
1367
+ "version_normalized": "5.4.9.0",
1368
+ "source": {
1369
+ "type": "git",
1370
+ "url": "https://github.com/symfony/http-client.git",
1371
+ "reference": "dc0b15e42b762c040761c1eb9ce86a55d47cf672"
1372
+ },
1373
+ "dist": {
1374
+ "type": "zip",
1375
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/dc0b15e42b762c040761c1eb9ce86a55d47cf672",
1376
+ "reference": "dc0b15e42b762c040761c1eb9ce86a55d47cf672",
1377
+ "shasum": ""
1378
+ },
1379
+ "require": {
1380
+ "php": ">=7.2.5",
1381
+ "psr/log": "^1|^2|^3",
1382
+ "symfony/deprecation-contracts": "^2.1|^3",
1383
+ "symfony/http-client-contracts": "^2.4",
1384
+ "symfony/polyfill-php73": "^1.11",
1385
+ "symfony/polyfill-php80": "^1.16",
1386
+ "symfony/service-contracts": "^1.0|^2|^3"
1387
+ },
1388
+ "provide": {
1389
+ "php-http/async-client-implementation": "*",
1390
+ "php-http/client-implementation": "*",
1391
+ "psr/http-client-implementation": "1.0",
1392
+ "symfony/http-client-implementation": "2.4"
1393
+ },
1394
+ "require-dev": {
1395
+ "amphp/amp": "^2.5",
1396
+ "amphp/http-client": "^4.2.1",
1397
+ "amphp/http-tunnel": "^1.0",
1398
+ "amphp/socket": "^1.1",
1399
+ "guzzlehttp/promises": "^1.4",
1400
+ "nyholm/psr7": "^1.0",
1401
+ "php-http/httplug": "^1.0|^2.0",
1402
+ "psr/http-client": "^1.0",
1403
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
1404
+ "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0",
1405
+ "symfony/process": "^4.4|^5.0|^6.0",
1406
+ "symfony/stopwatch": "^4.4|^5.0|^6.0"
1407
+ },
1408
+ "time": "2022-05-21T08:57:05+00:00",
1409
+ "type": "library",
1410
+ "installation-source": "dist",
1411
+ "autoload": {
1412
+ "psr-4": {
1413
+ "Symfony\\Component\\HttpClient\\": ""
1414
+ },
1415
+ "exclude-from-classmap": [
1416
+ "/Tests/"
1417
+ ]
1418
+ },
1419
+ "notification-url": "https://packagist.org/downloads/",
1420
+ "license": [
1421
+ "MIT"
1422
+ ],
1423
+ "authors": [
1424
+ {
1425
+ "name": "Nicolas Grekas",
1426
+ "email": "p@tchwork.com"
1427
+ },
1428
+ {
1429
+ "name": "Symfony Community",
1430
+ "homepage": "https://symfony.com/contributors"
1431
+ }
1432
+ ],
1433
+ "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
1434
+ "homepage": "https://symfony.com",
1435
+ "support": {
1436
+ "source": "https://github.com/symfony/http-client/tree/v5.4.9"
1437
+ },
1438
+ "funding": [
1439
+ {
1440
+ "url": "https://symfony.com/sponsor",
1441
+ "type": "custom"
1442
+ },
1443
+ {
1444
+ "url": "https://github.com/fabpot",
1445
+ "type": "github"
1446
+ },
1447
+ {
1448
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1449
+ "type": "tidelift"
1450
+ }
1451
+ ],
1452
+ "install-path": "../symfony/http-client"
1453
+ },
1454
+ {
1455
+ "name": "symfony/http-client-contracts",
1456
+ "version": "v2.5.1",
1457
+ "version_normalized": "2.5.1.0",
1458
+ "source": {
1459
+ "type": "git",
1460
+ "url": "https://github.com/symfony/http-client-contracts.git",
1461
+ "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5"
1462
+ },
1463
+ "dist": {
1464
+ "type": "zip",
1465
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1a4f708e4e87f335d1b1be6148060739152f0bd5",
1466
+ "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5",
1467
+ "shasum": ""
1468
+ },
1469
+ "require": {
1470
+ "php": ">=7.2.5"
1471
+ },
1472
+ "suggest": {
1473
+ "symfony/http-client-implementation": ""
1474
+ },
1475
+ "time": "2022-03-13T20:07:29+00:00",
1476
+ "type": "library",
1477
+ "extra": {
1478
+ "branch-alias": {
1479
+ "dev-main": "2.5-dev"
1480
+ },
1481
+ "thanks": {
1482
+ "name": "symfony/contracts",
1483
+ "url": "https://github.com/symfony/contracts"
1484
+ }
1485
+ },
1486
+ "installation-source": "dist",
1487
+ "autoload": {
1488
+ "psr-4": {
1489
+ "Symfony\\Contracts\\HttpClient\\": ""
1490
+ }
1491
+ },
1492
+ "notification-url": "https://packagist.org/downloads/",
1493
+ "license": [
1494
+ "MIT"
1495
+ ],
1496
+ "authors": [
1497
+ {
1498
+ "name": "Nicolas Grekas",
1499
+ "email": "p@tchwork.com"
1500
+ },
1501
+ {
1502
+ "name": "Symfony Community",
1503
+ "homepage": "https://symfony.com/contributors"
1504
+ }
1505
+ ],
1506
+ "description": "Generic abstractions related to HTTP clients",
1507
+ "homepage": "https://symfony.com",
1508
+ "keywords": [
1509
+ "abstractions",
1510
+ "contracts",
1511
+ "decoupling",
1512
+ "interfaces",
1513
+ "interoperability",
1514
+ "standards"
1515
+ ],
1516
+ "support": {
1517
+ "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.1"
1518
+ },
1519
+ "funding": [
1520
+ {
1521
+ "url": "https://symfony.com/sponsor",
1522
+ "type": "custom"
1523
+ },
1524
+ {
1525
+ "url": "https://github.com/fabpot",
1526
+ "type": "github"
1527
+ },
1528
+ {
1529
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1530
+ "type": "tidelift"
1531
+ }
1532
+ ],
1533
+ "install-path": "../symfony/http-client-contracts"
1534
+ },
1535
+ {
1536
+ "name": "symfony/options-resolver",
1537
+ "version": "v5.4.3",
1538
+ "version_normalized": "5.4.3.0",
1539
+ "source": {
1540
+ "type": "git",
1541
+ "url": "https://github.com/symfony/options-resolver.git",
1542
+ "reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8"
1543
+ },
1544
+ "dist": {
1545
+ "type": "zip",
1546
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/cc1147cb11af1b43f503ac18f31aa3bec213aba8",
1547
+ "reference": "cc1147cb11af1b43f503ac18f31aa3bec213aba8",
1548
+ "shasum": ""
1549
+ },
1550
+ "require": {
1551
+ "php": ">=7.2.5",
1552
+ "symfony/deprecation-contracts": "^2.1|^3",
1553
+ "symfony/polyfill-php73": "~1.0",
1554
+ "symfony/polyfill-php80": "^1.16"
1555
+ },
1556
+ "time": "2022-01-02T09:53:40+00:00",
1557
+ "type": "library",
1558
+ "installation-source": "dist",
1559
+ "autoload": {
1560
+ "psr-4": {
1561
+ "Symfony\\Component\\OptionsResolver\\": ""
1562
+ },
1563
+ "exclude-from-classmap": [
1564
+ "/Tests/"
1565
+ ]
1566
+ },
1567
+ "notification-url": "https://packagist.org/downloads/",
1568
+ "license": [
1569
+ "MIT"
1570
+ ],
1571
+ "authors": [
1572
+ {
1573
+ "name": "Fabien Potencier",
1574
+ "email": "fabien@symfony.com"
1575
+ },
1576
+ {
1577
+ "name": "Symfony Community",
1578
+ "homepage": "https://symfony.com/contributors"
1579
+ }
1580
+ ],
1581
+ "description": "Provides an improved replacement for the array_replace PHP function",
1582
+ "homepage": "https://symfony.com",
1583
+ "keywords": [
1584
+ "config",
1585
+ "configuration",
1586
+ "options"
1587
+ ],
1588
+ "support": {
1589
+ "source": "https://github.com/symfony/options-resolver/tree/v5.4.3"
1590
+ },
1591
+ "funding": [
1592
+ {
1593
+ "url": "https://symfony.com/sponsor",
1594
+ "type": "custom"
1595
+ },
1596
+ {
1597
+ "url": "https://github.com/fabpot",
1598
+ "type": "github"
1599
+ },
1600
+ {
1601
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1602
+ "type": "tidelift"
1603
+ }
1604
+ ],
1605
+ "install-path": "../symfony/options-resolver"
1606
+ },
1607
+ {
1608
+ "name": "symfony/polyfill-php73",
1609
+ "version": "v1.26.0",
1610
+ "version_normalized": "1.26.0.0",
1611
+ "source": {
1612
+ "type": "git",
1613
+ "url": "https://github.com/symfony/polyfill-php73.git",
1614
+ "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
1615
+ },
1616
+ "dist": {
1617
+ "type": "zip",
1618
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
1619
+ "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
1620
+ "shasum": ""
1621
+ },
1622
+ "require": {
1623
+ "php": ">=7.1"
1624
+ },
1625
+ "time": "2022-05-24T11:49:31+00:00",
1626
+ "type": "library",
1627
+ "extra": {
1628
+ "branch-alias": {
1629
+ "dev-main": "1.26-dev"
1630
+ },
1631
+ "thanks": {
1632
+ "name": "symfony/polyfill",
1633
+ "url": "https://github.com/symfony/polyfill"
1634
+ }
1635
+ },
1636
+ "installation-source": "dist",
1637
+ "autoload": {
1638
+ "files": [
1639
+ "bootstrap.php"
1640
+ ],
1641
+ "psr-4": {
1642
+ "Symfony\\Polyfill\\Php73\\": ""
1643
+ },
1644
+ "classmap": [
1645
+ "Resources/stubs"
1646
+ ]
1647
+ },
1648
+ "notification-url": "https://packagist.org/downloads/",
1649
+ "license": [
1650
+ "MIT"
1651
+ ],
1652
+ "authors": [
1653
+ {
1654
+ "name": "Nicolas Grekas",
1655
+ "email": "p@tchwork.com"
1656
+ },
1657
+ {
1658
+ "name": "Symfony Community",
1659
+ "homepage": "https://symfony.com/contributors"
1660
+ }
1661
+ ],
1662
+ "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
1663
+ "homepage": "https://symfony.com",
1664
+ "keywords": [
1665
+ "compatibility",
1666
+ "polyfill",
1667
+ "portable",
1668
+ "shim"
1669
+ ],
1670
+ "support": {
1671
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
1672
+ },
1673
+ "funding": [
1674
+ {
1675
+ "url": "https://symfony.com/sponsor",
1676
+ "type": "custom"
1677
+ },
1678
+ {
1679
+ "url": "https://github.com/fabpot",
1680
+ "type": "github"
1681
+ },
1682
+ {
1683
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1684
+ "type": "tidelift"
1685
+ }
1686
+ ],
1687
+ "install-path": "../symfony/polyfill-php73"
1688
+ },
1689
+ {
1690
+ "name": "symfony/polyfill-php80",
1691
+ "version": "v1.26.0",
1692
+ "version_normalized": "1.26.0.0",
1693
+ "source": {
1694
+ "type": "git",
1695
+ "url": "https://github.com/symfony/polyfill-php80.git",
1696
+ "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
1697
+ },
1698
+ "dist": {
1699
+ "type": "zip",
1700
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
1701
+ "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
1702
+ "shasum": ""
1703
+ },
1704
+ "require": {
1705
+ "php": ">=7.1"
1706
+ },
1707
+ "time": "2022-05-10T07:21:04+00:00",
1708
+ "type": "library",
1709
+ "extra": {
1710
+ "branch-alias": {
1711
+ "dev-main": "1.26-dev"
1712
+ },
1713
+ "thanks": {
1714
+ "name": "symfony/polyfill",
1715
+ "url": "https://github.com/symfony/polyfill"
1716
+ }
1717
+ },
1718
+ "installation-source": "dist",
1719
+ "autoload": {
1720
+ "files": [
1721
+ "bootstrap.php"
1722
+ ],
1723
+ "psr-4": {
1724
+ "Symfony\\Polyfill\\Php80\\": ""
1725
+ },
1726
+ "classmap": [
1727
+ "Resources/stubs"
1728
+ ]
1729
+ },
1730
+ "notification-url": "https://packagist.org/downloads/",
1731
+ "license": [
1732
+ "MIT"
1733
+ ],
1734
+ "authors": [
1735
+ {
1736
+ "name": "Ion Bazan",
1737
+ "email": "ion.bazan@gmail.com"
1738
+ },
1739
+ {
1740
+ "name": "Nicolas Grekas",
1741
+ "email": "p@tchwork.com"
1742
+ },
1743
+ {
1744
+ "name": "Symfony Community",
1745
+ "homepage": "https://symfony.com/contributors"
1746
+ }
1747
+ ],
1748
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
1749
+ "homepage": "https://symfony.com",
1750
+ "keywords": [
1751
+ "compatibility",
1752
+ "polyfill",
1753
+ "portable",
1754
+ "shim"
1755
+ ],
1756
+ "support": {
1757
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
1758
+ },
1759
+ "funding": [
1760
+ {
1761
+ "url": "https://symfony.com/sponsor",
1762
+ "type": "custom"
1763
+ },
1764
+ {
1765
+ "url": "https://github.com/fabpot",
1766
+ "type": "github"
1767
+ },
1768
+ {
1769
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1770
+ "type": "tidelift"
1771
+ }
1772
+ ],
1773
+ "install-path": "../symfony/polyfill-php80"
1774
+ },
1775
+ {
1776
+ "name": "symfony/polyfill-uuid",
1777
+ "version": "v1.26.0",
1778
+ "version_normalized": "1.26.0.0",
1779
+ "source": {
1780
+ "type": "git",
1781
+ "url": "https://github.com/symfony/polyfill-uuid.git",
1782
+ "reference": "a41886c1c81dc075a09c71fe6db5b9d68c79de23"
1783
+ },
1784
+ "dist": {
1785
+ "type": "zip",
1786
+ "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/a41886c1c81dc075a09c71fe6db5b9d68c79de23",
1787
+ "reference": "a41886c1c81dc075a09c71fe6db5b9d68c79de23",
1788
+ "shasum": ""
1789
+ },
1790
+ "require": {
1791
+ "php": ">=7.1"
1792
+ },
1793
+ "provide": {
1794
+ "ext-uuid": "*"
1795
+ },
1796
+ "suggest": {
1797
+ "ext-uuid": "For best performance"
1798
+ },
1799
+ "time": "2022-05-24T11:49:31+00:00",
1800
+ "type": "library",
1801
+ "extra": {
1802
+ "branch-alias": {
1803
+ "dev-main": "1.26-dev"
1804
+ },
1805
+ "thanks": {
1806
+ "name": "symfony/polyfill",
1807
+ "url": "https://github.com/symfony/polyfill"
1808
+ }
1809
+ },
1810
+ "installation-source": "dist",
1811
+ "autoload": {
1812
+ "files": [
1813
+ "bootstrap.php"
1814
+ ],
1815
+ "psr-4": {
1816
+ "Symfony\\Polyfill\\Uuid\\": ""
1817
+ }
1818
+ },
1819
+ "notification-url": "https://packagist.org/downloads/",
1820
+ "license": [
1821
+ "MIT"
1822
+ ],
1823
+ "authors": [
1824
+ {
1825
+ "name": "Grégoire Pineau",
1826
+ "email": "lyrixx@lyrixx.info"
1827
+ },
1828
+ {
1829
+ "name": "Symfony Community",
1830
+ "homepage": "https://symfony.com/contributors"
1831
+ }
1832
+ ],
1833
+ "description": "Symfony polyfill for uuid functions",
1834
+ "homepage": "https://symfony.com",
1835
+ "keywords": [
1836
+ "compatibility",
1837
+ "polyfill",
1838
+ "portable",
1839
+ "uuid"
1840
+ ],
1841
+ "support": {
1842
+ "source": "https://github.com/symfony/polyfill-uuid/tree/v1.26.0"
1843
+ },
1844
+ "funding": [
1845
+ {
1846
+ "url": "https://symfony.com/sponsor",
1847
+ "type": "custom"
1848
+ },
1849
+ {
1850
+ "url": "https://github.com/fabpot",
1851
+ "type": "github"
1852
+ },
1853
+ {
1854
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1855
+ "type": "tidelift"
1856
+ }
1857
+ ],
1858
+ "install-path": "../symfony/polyfill-uuid"
1859
+ },
1860
+ {
1861
+ "name": "symfony/service-contracts",
1862
+ "version": "v2.5.1",
1863
+ "version_normalized": "2.5.1.0",
1864
+ "source": {
1865
+ "type": "git",
1866
+ "url": "https://github.com/symfony/service-contracts.git",
1867
+ "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c"
1868
+ },
1869
+ "dist": {
1870
+ "type": "zip",
1871
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
1872
+ "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
1873
+ "shasum": ""
1874
+ },
1875
+ "require": {
1876
+ "php": ">=7.2.5",
1877
+ "psr/container": "^1.1",
1878
+ "symfony/deprecation-contracts": "^2.1|^3"
1879
+ },
1880
+ "conflict": {
1881
+ "ext-psr": "<1.1|>=2"
1882
+ },
1883
+ "suggest": {
1884
+ "symfony/service-implementation": ""
1885
+ },
1886
+ "time": "2022-03-13T20:07:29+00:00",
1887
+ "type": "library",
1888
+ "extra": {
1889
+ "branch-alias": {
1890
+ "dev-main": "2.5-dev"
1891
+ },
1892
+ "thanks": {
1893
+ "name": "symfony/contracts",
1894
+ "url": "https://github.com/symfony/contracts"
1895
+ }
1896
+ },
1897
+ "installation-source": "dist",
1898
+ "autoload": {
1899
+ "psr-4": {
1900
+ "Symfony\\Contracts\\Service\\": ""
1901
+ }
1902
+ },
1903
+ "notification-url": "https://packagist.org/downloads/",
1904
+ "license": [
1905
+ "MIT"
1906
+ ],
1907
+ "authors": [
1908
+ {
1909
+ "name": "Nicolas Grekas",
1910
+ "email": "p@tchwork.com"
1911
+ },
1912
+ {
1913
+ "name": "Symfony Community",
1914
+ "homepage": "https://symfony.com/contributors"
1915
+ }
1916
+ ],
1917
+ "description": "Generic abstractions related to writing services",
1918
+ "homepage": "https://symfony.com",
1919
+ "keywords": [
1920
+ "abstractions",
1921
+ "contracts",
1922
+ "decoupling",
1923
+ "interfaces",
1924
+ "interoperability",
1925
+ "standards"
1926
+ ],
1927
+ "support": {
1928
+ "source": "https://github.com/symfony/service-contracts/tree/v2.5.1"
1929
+ },
1930
+ "funding": [
1931
+ {
1932
+ "url": "https://symfony.com/sponsor",
1933
+ "type": "custom"
1934
+ },
1935
+ {
1936
+ "url": "https://github.com/fabpot",
1937
+ "type": "github"
1938
+ },
1939
+ {
1940
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1941
+ "type": "tidelift"
1942
+ }
1943
+ ],
1944
+ "install-path": "../symfony/service-contracts"
1945
+ }
1946
+ ],
1947
+ "dev": true,
1948
+ "dev-package-names": []
1949
+ }
src/vendor/composer/installed.php ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php return array(
2
+ 'root' => array(
3
+ 'pretty_version' => 'dev-master',
4
+ 'version' => 'dev-master',
5
+ 'type' => 'library',
6
+ 'install_path' => __DIR__ . '/../../',
7
+ 'aliases' => array(),
8
+ 'reference' => 'fb3ccded0384ee9d86c8ecb0ac0cf84a969f93e6',
9
+ 'name' => '__root__',
10
+ 'dev' => true,
11
+ ),
12
+ 'versions' => array(
13
+ '__root__' => array(
14
+ 'pretty_version' => 'dev-master',
15
+ 'version' => 'dev-master',
16
+ 'type' => 'library',
17
+ 'install_path' => __DIR__ . '/../../',
18
+ 'aliases' => array(),
19
+ 'reference' => 'fb3ccded0384ee9d86c8ecb0ac0cf84a969f93e6',
20
+ 'dev_requirement' => false,
21
+ ),
22
+ 'clue/stream-filter' => array(
23
+ 'pretty_version' => 'v1.6.0',
24
+ 'version' => '1.6.0.0',
25
+ 'type' => 'library',
26
+ 'install_path' => __DIR__ . '/../clue/stream-filter',
27
+ 'aliases' => array(),
28
+ 'reference' => 'd6169430c7731d8509da7aecd0af756a5747b78e',
29
+ 'dev_requirement' => false,
30
+ ),
31
+ 'guzzlehttp/promises' => array(
32
+ 'pretty_version' => '1.5.1',
33
+ 'version' => '1.5.1.0',
34
+ 'type' => 'library',
35
+ 'install_path' => __DIR__ . '/../guzzlehttp/promises',
36
+ 'aliases' => array(),
37
+ 'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da',
38
+ 'dev_requirement' => false,
39
+ ),
40
+ 'guzzlehttp/psr7' => array(
41
+ 'pretty_version' => '2.3.0',
42
+ 'version' => '2.3.0.0',
43
+ 'type' => 'library',
44
+ 'install_path' => __DIR__ . '/../guzzlehttp/psr7',
45
+ 'aliases' => array(),
46
+ 'reference' => '83260bb50b8fc753c72d14dc1621a2dac31877ee',
47
+ 'dev_requirement' => false,
48
+ ),
49
+ 'http-interop/http-factory-guzzle' => array(
50
+ 'pretty_version' => '1.2.0',
51
+ 'version' => '1.2.0.0',
52
+ 'type' => 'library',
53
+ 'install_path' => __DIR__ . '/../http-interop/http-factory-guzzle',
54
+ 'aliases' => array(),
55
+ 'reference' => '8f06e92b95405216b237521cc64c804dd44c4a81',
56
+ 'dev_requirement' => false,
57
+ ),
58
+ 'jean85/pretty-package-versions' => array(
59
+ 'pretty_version' => '2.0.5',
60
+ 'version' => '2.0.5.0',
61
+ 'type' => 'library',
62
+ 'install_path' => __DIR__ . '/../jean85/pretty-package-versions',
63
+ 'aliases' => array(),
64
+ 'reference' => 'ae547e455a3d8babd07b96966b17d7fd21d9c6af',
65
+ 'dev_requirement' => false,
66
+ ),
67
+ 'php-http/async-client-implementation' => array(
68
+ 'dev_requirement' => false,
69
+ 'provided' => array(
70
+ 0 => '*',
71
+ ),
72
+ ),
73
+ 'php-http/client-common' => array(
74
+ 'pretty_version' => '2.5.0',
75
+ 'version' => '2.5.0.0',
76
+ 'type' => 'library',
77
+ 'install_path' => __DIR__ . '/../php-http/client-common',
78
+ 'aliases' => array(),
79
+ 'reference' => 'd135751167d57e27c74de674d6a30cef2dc8e054',
80
+ 'dev_requirement' => false,
81
+ ),
82
+ 'php-http/client-implementation' => array(
83
+ 'dev_requirement' => false,
84
+ 'provided' => array(
85
+ 0 => '*',
86
+ ),
87
+ ),
88
+ 'php-http/discovery' => array(
89
+ 'pretty_version' => '1.14.2',
90
+ 'version' => '1.14.2.0',
91
+ 'type' => 'library',
92
+ 'install_path' => __DIR__ . '/../php-http/discovery',
93
+ 'aliases' => array(),
94
+ 'reference' => 'c8d48852fbc052454af42f6de27635ddd916b959',
95
+ 'dev_requirement' => false,
96
+ ),
97
+ 'php-http/httplug' => array(
98
+ 'pretty_version' => '2.3.0',
99
+ 'version' => '2.3.0.0',
100
+ 'type' => 'library',
101
+ 'install_path' => __DIR__ . '/../php-http/httplug',
102
+ 'aliases' => array(),
103
+ 'reference' => 'f640739f80dfa1152533976e3c112477f69274eb',
104
+ 'dev_requirement' => false,
105
+ ),
106
+ 'php-http/message' => array(
107
+ 'pretty_version' => '1.13.0',
108
+ 'version' => '1.13.0.0',
109
+ 'type' => 'library',
110
+ 'install_path' => __DIR__ . '/../php-http/message',
111
+ 'aliases' => array(),
112
+ 'reference' => '7886e647a30a966a1a8d1dad1845b71ca8678361',
113
+ 'dev_requirement' => false,
114
+ ),
115
+ 'php-http/message-factory' => array(
116
+ 'pretty_version' => 'v1.0.2',
117
+ 'version' => '1.0.2.0',
118
+ 'type' => 'library',
119
+ 'install_path' => __DIR__ . '/../php-http/message-factory',
120
+ 'aliases' => array(),
121
+ 'reference' => 'a478cb11f66a6ac48d8954216cfed9aa06a501a1',
122
+ 'dev_requirement' => false,
123
+ ),
124
+ 'php-http/message-factory-implementation' => array(
125
+ 'dev_requirement' => false,
126
+ 'provided' => array(
127
+ 0 => '1.0',
128
+ ),
129
+ ),
130
+ 'php-http/promise' => array(
131
+ 'pretty_version' => '1.1.0',
132
+ 'version' => '1.1.0.0',
133
+ 'type' => 'library',
134
+ 'install_path' => __DIR__ . '/../php-http/promise',
135
+ 'aliases' => array(),
136
+ 'reference' => '4c4c1f9b7289a2ec57cde7f1e9762a5789506f88',
137
+ 'dev_requirement' => false,
138
+ ),
139
+ 'psr/container' => array(
140
+ 'pretty_version' => '1.1.2',
141
+ 'version' => '1.1.2.0',
142
+ 'type' => 'library',
143
+ 'install_path' => __DIR__ . '/../psr/container',
144
+ 'aliases' => array(),
145
+ 'reference' => '513e0666f7216c7459170d56df27dfcefe1689ea',
146
+ 'dev_requirement' => false,
147
+ ),
148
+ 'psr/http-client' => array(
149
+ 'pretty_version' => '1.0.1',
150
+ 'version' => '1.0.1.0',
151
+ 'type' => 'library',
152
+ 'install_path' => __DIR__ . '/../psr/http-client',
153
+ 'aliases' => array(),
154
+ 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
155
+ 'dev_requirement' => false,
156
+ ),
157
+ 'psr/http-client-implementation' => array(
158
+ 'dev_requirement' => false,
159
+ 'provided' => array(
160
+ 0 => '1.0',
161
+ ),
162
+ ),
163
+ 'psr/http-factory' => array(
164
+ 'pretty_version' => '1.0.1',
165
+ 'version' => '1.0.1.0',
166
+ 'type' => 'library',
167
+ 'install_path' => __DIR__ . '/../psr/http-factory',
168
+ 'aliases' => array(),
169
+ 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be',
170
+ 'dev_requirement' => false,
171
+ ),
172
+ 'psr/http-factory-implementation' => array(
173
+ 'dev_requirement' => false,
174
+ 'provided' => array(
175
+ 0 => '1.0',
176
+ 1 => '^1.0',
177
+ ),
178
+ ),
179
+ 'psr/http-message' => array(
180
+ 'pretty_version' => '1.0.1',
181
+ 'version' => '1.0.1.0',
182
+ 'type' => 'library',
183
+ 'install_path' => __DIR__ . '/../psr/http-message',
184
+ 'aliases' => array(),
185
+ 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
186
+ 'dev_requirement' => false,
187
+ ),
188
+ 'psr/http-message-implementation' => array(
189
+ 'dev_requirement' => false,
190
+ 'provided' => array(
191
+ 0 => '1.0',
192
+ ),
193
+ ),
194
+ 'psr/log' => array(
195
+ 'pretty_version' => '1.1.4',
196
+ 'version' => '1.1.4.0',
197
+ 'type' => 'library',
198
+ 'install_path' => __DIR__ . '/../psr/log',
199
+ 'aliases' => array(),
200
+ 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
201
+ 'dev_requirement' => false,
202
+ ),
203
+ 'ralouphie/getallheaders' => array(
204
+ 'pretty_version' => '3.0.3',
205
+ 'version' => '3.0.3.0',
206
+ 'type' => 'library',
207
+ 'install_path' => __DIR__ . '/../ralouphie/getallheaders',
208
+ 'aliases' => array(),
209
+ 'reference' => '120b605dfeb996808c31b6477290a714d356e822',
210
+ 'dev_requirement' => false,
211
+ ),
212
+ 'sentry/sdk' => array(
213
+ 'pretty_version' => '3.2.0',
214
+ 'version' => '3.2.0.0',
215
+ 'type' => 'metapackage',
216
+ 'install_path' => NULL,
217
+ 'aliases' => array(),
218
+ 'reference' => '6d78bd83b43efbb52f81d6824f4af344fa9ba292',
219
+ 'dev_requirement' => false,
220
+ ),
221
+ 'sentry/sentry' => array(
222
+ 'pretty_version' => '3.6.0',
223
+ 'version' => '3.6.0.0',
224
+ 'type' => 'library',
225
+ 'install_path' => __DIR__ . '/../sentry/sentry',
226
+ 'aliases' => array(),
227
+ 'reference' => '6d1a6ee29c558be373bfe08d454a3c116c02dd0d',
228
+ 'dev_requirement' => false,
229
+ ),
230
+ 'symfony/deprecation-contracts' => array(
231
+ 'pretty_version' => 'v2.5.1',
232
+ 'version' => '2.5.1.0',
233
+ 'type' => 'library',
234
+ 'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
235
+ 'aliases' => array(),
236
+ 'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
237
+ 'dev_requirement' => false,
238
+ ),
239
+ 'symfony/http-client' => array(
240
+ 'pretty_version' => 'v5.4.9',
241
+ 'version' => '5.4.9.0',
242
+ 'type' => 'library',
243
+ 'install_path' => __DIR__ . '/../symfony/http-client',
244
+ 'aliases' => array(),
245
+ 'reference' => 'dc0b15e42b762c040761c1eb9ce86a55d47cf672',
246
+ 'dev_requirement' => false,
247
+ ),
248
+ 'symfony/http-client-contracts' => array(
249
+ 'pretty_version' => 'v2.5.1',
250
+ 'version' => '2.5.1.0',
251
+ 'type' => 'library',
252
+ 'install_path' => __DIR__ . '/../symfony/http-client-contracts',
253
+ 'aliases' => array(),
254
+ 'reference' => '1a4f708e4e87f335d1b1be6148060739152f0bd5',
255
+ 'dev_requirement' => false,
256
+ ),
257
+ 'symfony/http-client-implementation' => array(
258
+ 'dev_requirement' => false,
259
+ 'provided' => array(
260
+ 0 => '2.4',
261
+ ),
262
+ ),
263
+ 'symfony/options-resolver' => array(
264
+ 'pretty_version' => 'v5.4.3',
265
+ 'version' => '5.4.3.0',
266
+ 'type' => 'library',
267
+ 'install_path' => __DIR__ . '/../symfony/options-resolver',
268
+ 'aliases' => array(),
269
+ 'reference' => 'cc1147cb11af1b43f503ac18f31aa3bec213aba8',
270
+ 'dev_requirement' => false,
271
+ ),
272
+ 'symfony/polyfill-php73' => array(
273
+ 'pretty_version' => 'v1.26.0',
274
+ 'version' => '1.26.0.0',
275
+ 'type' => 'library',
276
+ 'install_path' => __DIR__ . '/../symfony/polyfill-php73',
277
+ 'aliases' => array(),
278
+ 'reference' => 'e440d35fa0286f77fb45b79a03fedbeda9307e85',
279
+ 'dev_requirement' => false,
280
+ ),
281
+ 'symfony/polyfill-php80' => array(
282
+ 'pretty_version' => 'v1.26.0',
283
+ 'version' => '1.26.0.0',
284
+ 'type' => 'library',
285
+ 'install_path' => __DIR__ . '/../symfony/polyfill-php80',
286
+ 'aliases' => array(),
287
+ 'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
288
+ 'dev_requirement' => false,
289
+ ),
290
+ 'symfony/polyfill-uuid' => array(
291
+ 'pretty_version' => 'v1.26.0',
292
+ 'version' => '1.26.0.0',
293
+ 'type' => 'library',
294
+ 'install_path' => __DIR__ . '/../symfony/polyfill-uuid',
295
+ 'aliases' => array(),
296
+ 'reference' => 'a41886c1c81dc075a09c71fe6db5b9d68c79de23',
297
+ 'dev_requirement' => false,
298
+ ),
299
+ 'symfony/service-contracts' => array(
300
+ 'pretty_version' => 'v2.5.1',
301
+ 'version' => '2.5.1.0',
302
+ 'type' => 'library',
303
+ 'install_path' => __DIR__ . '/../symfony/service-contracts',
304
+ 'aliases' => array(),
305
+ 'reference' => '24d9dc654b83e91aa59f9d167b131bc3b5bea24c',
306
+ 'dev_requirement' => false,
307
+ ),
308
+ ),
309
+ );
src/vendor/composer/platform_check.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // platform_check.php @generated by Composer
4
+
5
+ $issues = array();
6
+
7
+ if (!(PHP_VERSION_ID >= 70400)) {
8
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
9
+ }
10
+
11
+ if ($issues) {
12
+ if (!headers_sent()) {
13
+ header('HTTP/1.1 500 Internal Server Error');
14
+ }
15
+ if (!ini_get('display_errors')) {
16
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
17
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
18
+ } elseif (!headers_sent()) {
19
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
20
+ }
21
+ }
22
+ trigger_error(
23
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
24
+ E_USER_ERROR
25
+ );
26
+ }
src/vendor/guzzlehttp/promises/CHANGELOG.md ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CHANGELOG
2
+
3
+ ## 1.5.1 - 2021-10-22
4
+
5
+ ### Fixed
6
+
7
+ - Revert "Call handler when waiting on fulfilled/rejected Promise"
8
+ - Fix pool memory leak when empty array of promises provided
9
+
10
+ ## 1.5.0 - 2021-10-07
11
+
12
+ ### Changed
13
+
14
+ - Call handler when waiting on fulfilled/rejected Promise
15
+
16
+ ### Fixed
17
+
18
+ - Fix manually settle promises generated with Utils::task
19
+
20
+ ## 1.4.1 - 2021-02-18
21
+
22
+ ### Fixed
23
+
24
+ - Fixed `each_limit` skipping promises and failing
25
+
26
+ ## 1.4.0 - 2020-09-30
27
+
28
+ ### Added
29
+
30
+ - Support for PHP 8
31
+ - Optional `$recursive` flag to `all`
32
+ - Replaced functions by static methods
33
+
34
+ ### Fixed
35
+
36
+ - Fix empty `each` processing
37
+ - Fix promise handling for Iterators of non-unique keys
38
+ - Fixed `method_exists` crashes on PHP 8
39
+ - Memory leak on exceptions
40
+
41
+
42
+ ## 1.3.1 - 2016-12-20
43
+
44
+ ### Fixed
45
+
46
+ - `wait()` foreign promise compatibility
47
+
48
+
49
+ ## 1.3.0 - 2016-11-18
50
+
51
+ ### Added
52
+
53
+ - Adds support for custom task queues.
54
+
55
+ ### Fixed
56
+
57
+ - Fixed coroutine promise memory leak.
58
+
59
+
60
+ ## 1.2.0 - 2016-05-18
61
+
62
+ ### Changed
63
+
64
+ - Update to now catch `\Throwable` on PHP 7+
65
+
66
+
67
+ ## 1.1.0 - 2016-03-07
68
+
69
+ ### Changed
70
+
71
+ - Update EachPromise to prevent recurring on a iterator when advancing, as this
72
+ could trigger fatal generator errors.
73
+ - Update Promise to allow recursive waiting without unwrapping exceptions.
74
+
75
+
76
+ ## 1.0.3 - 2015-10-15
77
+
78
+ ### Changed
79
+
80
+ - Update EachPromise to immediately resolve when the underlying promise iterator
81
+ is empty. Previously, such a promise would throw an exception when its `wait`
82
+ function was called.
83
+
84
+
85
+ ## 1.0.2 - 2015-05-15
86
+
87
+ ### Changed
88
+
89
+ - Conditionally require functions.php.
90
+
91
+
92
+ ## 1.0.1 - 2015-06-24
93
+
94
+ ### Changed
95
+
96
+ - Updating EachPromise to call next on the underlying promise iterator as late
97
+ as possible to ensure that generators that generate new requests based on
98
+ callbacks are not iterated until after callbacks are invoked.
99
+
100
+
101
+ ## 1.0.0 - 2015-05-12
102
+
103
+ - Initial release
src/vendor/guzzlehttp/promises/LICENSE ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Michael Dowling <mtdowling@gmail.com>
4
+ Copyright (c) 2015 Graham Campbell <hello@gjcampbell.co.uk>
5
+ Copyright (c) 2017 Tobias Schultze <webmaster@tubo-world.de>
6
+ Copyright (c) 2020 Tobias Nyholm <tobias.nyholm@gmail.com>
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.
src/vendor/guzzlehttp/promises/Makefile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ all: clean test
2
+
3
+ test:
4
+ vendor/bin/phpunit
5
+
6
+ coverage:
7
+ vendor/bin/phpunit --coverage-html=artifacts/coverage
8
+
9
+ view-coverage:
10
+ open artifacts/coverage/index.html
11
+
12
+ clean:
13
+ rm -rf artifacts/*
src/vendor/guzzlehttp/promises/README.md ADDED
@@ -0,0 +1,547 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Guzzle Promises
2
+
3
+ [Promises/A+](https://promisesaplus.com/) implementation that handles promise
4
+ chaining and resolution iteratively, allowing for "infinite" promise chaining
5
+ while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
6
+ for a general introduction to promises.
7
+
8
+ - [Features](#features)
9
+ - [Quick start](#quick-start)
10
+ - [Synchronous wait](#synchronous-wait)
11
+ - [Cancellation](#cancellation)
12
+ - [API](#api)
13
+ - [Promise](#promise)
14
+ - [FulfilledPromise](#fulfilledpromise)
15
+ - [RejectedPromise](#rejectedpromise)
16
+ - [Promise interop](#promise-interop)
17
+ - [Implementation notes](#implementation-notes)
18
+
19
+
20
+ # Features
21
+
22
+ - [Promises/A+](https://promisesaplus.com/) implementation.
23
+ - Promise resolution and chaining is handled iteratively, allowing for
24
+ "infinite" promise chaining.
25
+ - Promises have a synchronous `wait` method.
26
+ - Promises can be cancelled.
27
+ - Works with any object that has a `then` function.
28
+ - C# style async/await coroutine promises using
29
+ `GuzzleHttp\Promise\Coroutine::of()`.
30
+
31
+
32
+ # Quick start
33
+
34
+ A *promise* represents the eventual result of an asynchronous operation. The
35
+ primary way of interacting with a promise is through its `then` method, which
36
+ registers callbacks to receive either a promise's eventual value or the reason
37
+ why the promise cannot be fulfilled.
38
+
39
+
40
+ ## Callbacks
41
+
42
+ Callbacks are registered with the `then` method by providing an optional
43
+ `$onFulfilled` followed by an optional `$onRejected` function.
44
+
45
+
46
+ ```php
47
+ use GuzzleHttp\Promise\Promise;
48
+
49
+ $promise = new Promise();
50
+ $promise->then(
51
+ // $onFulfilled
52
+ function ($value) {
53
+ echo 'The promise was fulfilled.';
54
+ },
55
+ // $onRejected
56
+ function ($reason) {
57
+ echo 'The promise was rejected.';
58
+ }
59
+ );
60
+ ```
61
+
62
+ *Resolving* a promise means that you either fulfill a promise with a *value* or
63
+ reject a promise with a *reason*. Resolving a promises triggers callbacks
64
+ registered with the promises's `then` method. These callbacks are triggered
65
+ only once and in the order in which they were added.
66
+
67
+
68
+ ## Resolving a promise
69
+
70
+ Promises are fulfilled using the `resolve($value)` method. Resolving a promise
71
+ with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
72
+ all of the onFulfilled callbacks (resolving a promise with a rejected promise
73
+ will reject the promise and trigger the `$onRejected` callbacks).
74
+
75
+ ```php
76
+ use GuzzleHttp\Promise\Promise;
77
+
78
+ $promise = new Promise();
79
+ $promise
80
+ ->then(function ($value) {
81
+ // Return a value and don't break the chain
82
+ return "Hello, " . $value;
83
+ })
84
+ // This then is executed after the first then and receives the value
85
+ // returned from the first then.
86
+ ->then(function ($value) {
87
+ echo $value;
88
+ });
89
+
90
+ // Resolving the promise triggers the $onFulfilled callbacks and outputs
91
+ // "Hello, reader."
92
+ $promise->resolve('reader.');
93
+ ```
94
+
95
+
96
+ ## Promise forwarding
97
+
98
+ Promises can be chained one after the other. Each then in the chain is a new
99
+ promise. The return value of a promise is what's forwarded to the next
100
+ promise in the chain. Returning a promise in a `then` callback will cause the
101
+ subsequent promises in the chain to only be fulfilled when the returned promise
102
+ has been fulfilled. The next promise in the chain will be invoked with the
103
+ resolved value of the promise.
104
+
105
+ ```php
106
+ use GuzzleHttp\Promise\Promise;
107
+
108
+ $promise = new Promise();
109
+ $nextPromise = new Promise();
110
+
111
+ $promise
112
+ ->then(function ($value) use ($nextPromise) {
113
+ echo $value;
114
+ return $nextPromise;
115
+ })
116
+ ->then(function ($value) {
117
+ echo $value;
118
+ });
119
+
120
+ // Triggers the first callback and outputs "A"
121
+ $promise->resolve('A');
122
+ // Triggers the second callback and outputs "B"
123
+ $nextPromise->resolve('B');
124
+ ```
125
+
126
+ ## Promise rejection
127
+
128
+ When a promise is rejected, the `$onRejected` callbacks are invoked with the
129
+ rejection reason.
130
+
131
+ ```php
132
+ use GuzzleHttp\Promise\Promise;
133
+
134
+ $promise = new Promise();
135
+ $promise->then(null, function ($reason) {
136
+ echo $reason;
137
+ });
138
+
139
+ $promise->reject('Error!');
140
+ // Outputs "Error!"
141
+ ```
142
+
143
+ ## Rejection forwarding
144
+
145
+ If an exception is thrown in an `$onRejected` callback, subsequent
146
+ `$onRejected` callbacks are invoked with the thrown exception as the reason.
147
+
148
+ ```php
149
+ use GuzzleHttp\Promise\Promise;
150
+
151
+ $promise = new Promise();
152
+ $promise->then(null, function ($reason) {
153
+ throw new Exception($reason);
154
+ })->then(null, function ($reason) {
155
+ assert($reason->getMessage() === 'Error!');
156
+ });
157
+
158
+ $promise->reject('Error!');
159
+ ```
160
+
161
+ You can also forward a rejection down the promise chain by returning a
162
+ `GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
163
+ `$onRejected` callback.
164
+
165
+ ```php
166
+ use GuzzleHttp\Promise\Promise;
167
+ use GuzzleHttp\Promise\RejectedPromise;
168
+
169
+ $promise = new Promise();
170
+ $promise->then(null, function ($reason) {
171
+ return new RejectedPromise($reason);
172
+ })->then(null, function ($reason) {
173
+ assert($reason === 'Error!');
174
+ });
175
+
176
+ $promise->reject('Error!');
177
+ ```
178
+
179
+ If an exception is not thrown in a `$onRejected` callback and the callback
180
+ does not return a rejected promise, downstream `$onFulfilled` callbacks are
181
+ invoked using the value returned from the `$onRejected` callback.
182
+
183
+ ```php
184
+ use GuzzleHttp\Promise\Promise;
185
+
186
+ $promise = new Promise();
187
+ $promise
188
+ ->then(null, function ($reason) {
189
+ return "It's ok";
190
+ })
191
+ ->then(function ($value) {
192
+ assert($value === "It's ok");
193
+ });
194
+
195
+ $promise->reject('Error!');
196
+ ```
197
+
198
+ # Synchronous wait
199
+
200
+ You can synchronously force promises to complete using a promise's `wait`
201
+ method. When creating a promise, you can provide a wait function that is used
202
+ to synchronously force a promise to complete. When a wait function is invoked
203
+ it is expected to deliver a value to the promise or reject the promise. If the
204
+ wait function does not deliver a value, then an exception is thrown. The wait
205
+ function provided to a promise constructor is invoked when the `wait` function
206
+ of the promise is called.
207
+
208
+ ```php
209
+ $promise = new Promise(function () use (&$promise) {
210
+ $promise->resolve('foo');
211
+ });
212
+
213
+ // Calling wait will return the value of the promise.
214
+ echo $promise->wait(); // outputs "foo"
215
+ ```
216
+
217
+ If an exception is encountered while invoking the wait function of a promise,
218
+ the promise is rejected with the exception and the exception is thrown.
219
+
220
+ ```php
221
+ $promise = new Promise(function () use (&$promise) {
222
+ throw new Exception('foo');
223
+ });
224
+
225
+ $promise->wait(); // throws the exception.
226
+ ```
227
+
228
+ Calling `wait` on a promise that has been fulfilled will not trigger the wait
229
+ function. It will simply return the previously resolved value.
230
+
231
+ ```php
232
+ $promise = new Promise(function () { die('this is not called!'); });
233
+ $promise->resolve('foo');
234
+ echo $promise->wait(); // outputs "foo"
235
+ ```
236
+
237
+ Calling `wait` on a promise that has been rejected will throw an exception. If
238
+ the rejection reason is an instance of `\Exception` the reason is thrown.
239
+ Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
240
+ can be obtained by calling the `getReason` method of the exception.
241
+
242
+ ```php
243
+ $promise = new Promise();
244
+ $promise->reject('foo');
245
+ $promise->wait();
246
+ ```
247
+
248
+ > PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
249
+
250
+
251
+ ## Unwrapping a promise
252
+
253
+ When synchronously waiting on a promise, you are joining the state of the
254
+ promise into the current state of execution (i.e., return the value of the
255
+ promise if it was fulfilled or throw an exception if it was rejected). This is
256
+ called "unwrapping" the promise. Waiting on a promise will by default unwrap
257
+ the promise state.
258
+
259
+ You can force a promise to resolve and *not* unwrap the state of the promise
260
+ by passing `false` to the first argument of the `wait` function:
261
+
262
+ ```php
263
+ $promise = new Promise();
264
+ $promise->reject('foo');
265
+ // This will not throw an exception. It simply ensures the promise has
266
+ // been resolved.
267
+ $promise->wait(false);
268
+ ```
269
+
270
+ When unwrapping a promise, the resolved value of the promise will be waited
271
+ upon until the unwrapped value is not a promise. This means that if you resolve
272
+ promise A with a promise B and unwrap promise A, the value returned by the
273
+ wait function will be the value delivered to promise B.
274
+
275
+ **Note**: when you do not unwrap the promise, no value is returned.
276
+
277
+
278
+ # Cancellation
279
+
280
+ You can cancel a promise that has not yet been fulfilled using the `cancel()`
281
+ method of a promise. When creating a promise you can provide an optional
282
+ cancel function that when invoked cancels the action of computing a resolution
283
+ of the promise.
284
+
285
+
286
+ # API
287
+
288
+
289
+ ## Promise
290
+
291
+ When creating a promise object, you can provide an optional `$waitFn` and
292
+ `$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
293
+ expected to resolve the promise. `$cancelFn` is a function with no arguments
294
+ that is expected to cancel the computation of a promise. It is invoked when the
295
+ `cancel()` method of a promise is called.
296
+
297
+ ```php
298
+ use GuzzleHttp\Promise\Promise;
299
+
300
+ $promise = new Promise(
301
+ function () use (&$promise) {
302
+ $promise->resolve('waited');
303
+ },
304
+ function () {
305
+ // do something that will cancel the promise computation (e.g., close
306
+ // a socket, cancel a database query, etc...)
307
+ }
308
+ );
309
+
310
+ assert('waited' === $promise->wait());
311
+ ```
312
+
313
+ A promise has the following methods:
314
+
315
+ - `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
316
+
317
+ Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.
318
+
319
+ - `otherwise(callable $onRejected) : PromiseInterface`
320
+
321
+ Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.
322
+
323
+ - `wait($unwrap = true) : mixed`
324
+
325
+ Synchronously waits on the promise to complete.
326
+
327
+ `$unwrap` controls whether or not the value of the promise is returned for a
328
+ fulfilled promise or if an exception is thrown if the promise is rejected.
329
+ This is set to `true` by default.
330
+
331
+ - `cancel()`
332
+
333
+ Attempts to cancel the promise if possible. The promise being cancelled and
334
+ the parent most ancestor that has not yet been resolved will also be
335
+ cancelled. Any promises waiting on the cancelled promise to resolve will also
336
+ be cancelled.
337
+
338
+ - `getState() : string`
339
+
340
+ Returns the state of the promise. One of `pending`, `fulfilled`, or
341
+ `rejected`.
342
+
343
+ - `resolve($value)`
344
+
345
+ Fulfills the promise with the given `$value`.
346
+
347
+ - `reject($reason)`
348
+
349
+ Rejects the promise with the given `$reason`.
350
+
351
+
352
+ ## FulfilledPromise
353
+
354
+ A fulfilled promise can be created to represent a promise that has been
355
+ fulfilled.
356
+
357
+ ```php
358
+ use GuzzleHttp\Promise\FulfilledPromise;
359
+
360
+ $promise = new FulfilledPromise('value');
361
+
362
+ // Fulfilled callbacks are immediately invoked.
363
+ $promise->then(function ($value) {
364
+ echo $value;
365
+ });
366
+ ```
367
+
368
+
369
+ ## RejectedPromise
370
+
371
+ A rejected promise can be created to represent a promise that has been
372
+ rejected.
373
+
374
+ ```php
375
+ use GuzzleHttp\Promise\RejectedPromise;
376
+
377
+ $promise = new RejectedPromise('Error');
378
+
379
+ // Rejected callbacks are immediately invoked.
380
+ $promise->then(null, function ($reason) {
381
+ echo $reason;
382
+ });
383
+ ```
384
+
385
+
386
+ # Promise interop
387
+
388
+ This library works with foreign promises that have a `then` method. This means
389
+ you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
390
+ for example. When a foreign promise is returned inside of a then method
391
+ callback, promise resolution will occur recursively.
392
+
393
+ ```php
394
+ // Create a React promise
395
+ $deferred = new React\Promise\Deferred();
396
+ $reactPromise = $deferred->promise();
397
+
398
+ // Create a Guzzle promise that is fulfilled with a React promise.
399
+ $guzzlePromise = new GuzzleHttp\Promise\Promise();
400
+ $guzzlePromise->then(function ($value) use ($reactPromise) {
401
+ // Do something something with the value...
402
+ // Return the React promise
403
+ return $reactPromise;
404
+ });
405
+ ```
406
+
407
+ Please note that wait and cancel chaining is no longer possible when forwarding
408
+ a foreign promise. You will need to wrap a third-party promise with a Guzzle
409
+ promise in order to utilize wait and cancel functions with foreign promises.
410
+
411
+
412
+ ## Event Loop Integration
413
+
414
+ In order to keep the stack size constant, Guzzle promises are resolved
415
+ asynchronously using a task queue. When waiting on promises synchronously, the
416
+ task queue will be automatically run to ensure that the blocking promise and
417
+ any forwarded promises are resolved. When using promises asynchronously in an
418
+ event loop, you will need to run the task queue on each tick of the loop. If
419
+ you do not run the task queue, then promises will not be resolved.
420
+
421
+ You can run the task queue using the `run()` method of the global task queue
422
+ instance.
423
+
424
+ ```php
425
+ // Get the global task queue
426
+ $queue = GuzzleHttp\Promise\Utils::queue();
427
+ $queue->run();
428
+ ```
429
+
430
+ For example, you could use Guzzle promises with React using a periodic timer:
431
+
432
+ ```php
433
+ $loop = React\EventLoop\Factory::create();
434
+ $loop->addPeriodicTimer(0, [$queue, 'run']);
435
+ ```
436
+
437
+ *TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
438
+
439
+
440
+ # Implementation notes
441
+
442
+
443
+ ## Promise resolution and chaining is handled iteratively
444
+
445
+ By shuffling pending handlers from one owner to another, promises are
446
+ resolved iteratively, allowing for "infinite" then chaining.
447
+
448
+ ```php
449
+ <?php
450
+ require 'vendor/autoload.php';
451
+
452
+ use GuzzleHttp\Promise\Promise;
453
+
454
+ $parent = new Promise();
455
+ $p = $parent;
456
+
457
+ for ($i = 0; $i < 1000; $i++) {
458
+ $p = $p->then(function ($v) {
459
+ // The stack size remains constant (a good thing)
460
+ echo xdebug_get_stack_depth() . ', ';
461
+ return $v + 1;
462
+ });
463
+ }
464
+
465
+ $parent->resolve(0);
466
+ var_dump($p->wait()); // int(1000)
467
+
468
+ ```
469
+
470
+ When a promise is fulfilled or rejected with a non-promise value, the promise
471
+ then takes ownership of the handlers of each child promise and delivers values
472
+ down the chain without using recursion.
473
+
474
+ When a promise is resolved with another promise, the original promise transfers
475
+ all of its pending handlers to the new promise. When the new promise is
476
+ eventually resolved, all of the pending handlers are delivered the forwarded
477
+ value.
478
+
479
+
480
+ ## A promise is the deferred.
481
+
482
+ Some promise libraries implement promises using a deferred object to represent
483
+ a computation and a promise object to represent the delivery of the result of
484
+ the computation. This is a nice separation of computation and delivery because
485
+ consumers of the promise cannot modify the value that will be eventually
486
+ delivered.
487
+
488
+ One side effect of being able to implement promise resolution and chaining
489
+ iteratively is that you need to be able for one promise to reach into the state
490
+ of another promise to shuffle around ownership of handlers. In order to achieve
491
+ this without making the handlers of a promise publicly mutable, a promise is
492
+ also the deferred value, allowing promises of the same parent class to reach
493
+ into and modify the private properties of promises of the same type. While this
494
+ does allow consumers of the value to modify the resolution or rejection of the
495
+ deferred, it is a small price to pay for keeping the stack size constant.
496
+
497
+ ```php
498
+ $promise = new Promise();
499
+ $promise->then(function ($value) { echo $value; });
500
+ // The promise is the deferred value, so you can deliver a value to it.
501
+ $promise->resolve('foo');
502
+ // prints "foo"
503
+ ```
504
+
505
+
506
+ ## Upgrading from Function API
507
+
508
+ 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:
509
+
510
+ | Original Function | Replacement Method |
511
+ |----------------|----------------|
512
+ | `queue` | `Utils::queue` |
513
+ | `task` | `Utils::task` |
514
+ | `promise_for` | `Create::promiseFor` |
515
+ | `rejection_for` | `Create::rejectionFor` |
516
+ | `exception_for` | `Create::exceptionFor` |
517
+ | `iter_for` | `Create::iterFor` |
518
+ | `inspect` | `Utils::inspect` |
519
+ | `inspect_all` | `Utils::inspectAll` |
520
+ | `unwrap` | `Utils::unwrap` |
521
+ | `all` | `Utils::all` |
522
+ | `some` | `Utils::some` |
523
+ | `any` | `Utils::any` |
524
+ | `settle` | `Utils::settle` |
525
+ | `each` | `Each::of` |
526
+ | `each_limit` | `Each::ofLimit` |
527
+ | `each_limit_all` | `Each::ofLimitAll` |
528
+ | `!is_fulfilled` | `Is::pending` |
529
+ | `is_fulfilled` | `Is::fulfilled` |
530
+ | `is_rejected` | `Is::rejected` |
531
+ | `is_settled` | `Is::settled` |
532
+ | `coroutine` | `Coroutine::of` |
533
+
534
+
535
+ ## Security
536
+
537
+ 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.
538
+
539
+ ## License
540
+
541
+ Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
542
+
543
+ ## For Enterprise
544
+
545
+ Available as part of the Tidelift Subscription
546
+
547
+ The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-promises?utm_source=packagist-guzzlehttp-promises&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
src/vendor/guzzlehttp/promises/composer.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "guzzlehttp/promises",
3
+ "description": "Guzzle promises library",
4
+ "keywords": ["promise"],
5
+ "license": "MIT",
6
+ "authors": [
7
+ {
8
+ "name": "Graham Campbell",
9
+ "email": "hello@gjcampbell.co.uk",
10
+ "homepage": "https://github.com/GrahamCampbell"
11
+ },
12
+ {
13
+ "name": "Michael Dowling",
14
+ "email": "mtdowling@gmail.com",
15
+ "homepage": "https://github.com/mtdowling"
16
+ },
17
+ {
18
+ "name": "Tobias Nyholm",
19
+ "email": "tobias.nyholm@gmail.com",
20
+ "homepage": "https://github.com/Nyholm"
21
+ },
22
+ {
23
+ "name": "Tobias Schultze",
24
+ "email": "webmaster@tubo-world.de",
25
+ "homepage": "https://github.com/Tobion"
26
+ }
27
+ ],
28
+ "require": {
29
+ "php": ">=5.5"
30
+ },
31
+ "require-dev": {
32
+ "symfony/phpunit-bridge": "^4.4 || ^5.1"
33
+ },
34
+ "autoload": {
35
+ "psr-4": {
36
+ "GuzzleHttp\\Promise\\": "src/"
37
+ },
38
+ "files": ["src/functions_include.php"]
39
+ },
40
+ "autoload-dev": {
41
+ "psr-4": {
42
+ "GuzzleHttp\\Promise\\Tests\\": "tests/"
43
+ }
44
+ },
45
+ "scripts": {
46
+ "test": "vendor/bin/simple-phpunit",
47
+ "test-ci": "vendor/bin/simple-phpunit --coverage-text"
48
+ },
49
+ "extra": {
50
+ "branch-alias": {
51
+ "dev-master": "1.5-dev"
52
+ }
53
+ },
54
+ "config": {
55
+ "preferred-install": "dist",
56
+ "sort-packages": true
57
+ }
58
+ }
src/vendor/guzzlehttp/promises/src/AggregateException.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * Exception thrown when too many errors occur in the some() or any() methods.
7
+ */
8
+ class AggregateException extends RejectionException
9
+ {
10
+ public function __construct($msg, array $reasons)
11
+ {
12
+ parent::__construct(
13
+ $reasons,
14
+ sprintf('%s; %d rejected promises', $msg, count($reasons))
15
+ );
16
+ }
17
+ }
src/vendor/guzzlehttp/promises/src/CancellationException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * Exception that is set as the reason for a promise that has been cancelled.
7
+ */
8
+ class CancellationException extends RejectionException
9
+ {
10
+ }
src/vendor/guzzlehttp/promises/src/Coroutine.php ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ use Exception;
6
+ use Generator;
7
+ use Throwable;
8
+
9
+ /**
10
+ * Creates a promise that is resolved using a generator that yields values or
11
+ * promises (somewhat similar to C#'s async keyword).
12
+ *
13
+ * When called, the Coroutine::of method will start an instance of the generator
14
+ * and returns a promise that is fulfilled with its final yielded value.
15
+ *
16
+ * Control is returned back to the generator when the yielded promise settles.
17
+ * This can lead to less verbose code when doing lots of sequential async calls
18
+ * with minimal processing in between.
19
+ *
20
+ * use GuzzleHttp\Promise;
21
+ *
22
+ * function createPromise($value) {
23
+ * return new Promise\FulfilledPromise($value);
24
+ * }
25
+ *
26
+ * $promise = Promise\Coroutine::of(function () {
27
+ * $value = (yield createPromise('a'));
28
+ * try {
29
+ * $value = (yield createPromise($value . 'b'));
30
+ * } catch (\Exception $e) {
31
+ * // The promise was rejected.
32
+ * }
33
+ * yield $value . 'c';
34
+ * });
35
+ *
36
+ * // Outputs "abc"
37
+ * $promise->then(function ($v) { echo $v; });
38
+ *
39
+ * @param callable $generatorFn Generator function to wrap into a promise.
40
+ *
41
+ * @return Promise
42
+ *
43
+ * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
44
+ */
45
+ final class Coroutine implements PromiseInterface
46
+ {
47
+ /**
48
+ * @var PromiseInterface|null
49
+ */
50
+ private $currentPromise;
51
+
52
+ /**
53
+ * @var Generator
54
+ */
55
+ private $generator;
56
+
57
+ /**
58
+ * @var Promise
59
+ */
60
+ private $result;
61
+
62
+ public function __construct(callable $generatorFn)
63
+ {
64
+ $this->generator = $generatorFn();
65
+ $this->result = new Promise(function () {
66
+ while (isset($this->currentPromise)) {
67
+ $this->currentPromise->wait();
68
+ }
69
+ });
70
+ try {
71
+ $this->nextCoroutine($this->generator->current());
72
+ } catch (\Exception $exception) {
73
+ $this->result->reject($exception);
74
+ } catch (Throwable $throwable) {
75
+ $this->result->reject($throwable);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Create a new coroutine.
81
+ *
82
+ * @return self
83
+ */
84
+ public static function of(callable $generatorFn)
85
+ {
86
+ return new self($generatorFn);
87
+ }
88
+
89
+ public function then(
90
+ callable $onFulfilled = null,
91
+ callable $onRejected = null
92
+ ) {
93
+ return $this->result->then($onFulfilled, $onRejected);
94
+ }
95
+
96
+ public function otherwise(callable $onRejected)
97
+ {
98
+ return $this->result->otherwise($onRejected);
99
+ }
100
+
101
+ public function wait($unwrap = true)
102
+ {
103
+ return $this->result->wait($unwrap);
104
+ }
105
+
106
+ public function getState()
107
+ {
108
+ return $this->result->getState();
109
+ }
110
+
111
+ public function resolve($value)
112
+ {
113
+ $this->result->resolve($value);
114
+ }
115
+
116
+ public function reject($reason)
117
+ {
118
+ $this->result->reject($reason);
119
+ }
120
+
121
+ public function cancel()
122
+ {
123
+ $this->currentPromise->cancel();
124
+ $this->result->cancel();
125
+ }
126
+
127
+ private function nextCoroutine($yielded)
128
+ {
129
+ $this->currentPromise = Create::promiseFor($yielded)
130
+ ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
131
+ }
132
+
133
+ /**
134
+ * @internal
135
+ */
136
+ public function _handleSuccess($value)
137
+ {
138
+ unset($this->currentPromise);
139
+ try {
140
+ $next = $this->generator->send($value);
141
+ if ($this->generator->valid()) {
142
+ $this->nextCoroutine($next);
143
+ } else {
144
+ $this->result->resolve($value);
145
+ }
146
+ } catch (Exception $exception) {
147
+ $this->result->reject($exception);
148
+ } catch (Throwable $throwable) {
149
+ $this->result->reject($throwable);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * @internal
155
+ */
156
+ public function _handleFailure($reason)
157
+ {
158
+ unset($this->currentPromise);
159
+ try {
160
+ $nextYield = $this->generator->throw(Create::exceptionFor($reason));
161
+ // The throw was caught, so keep iterating on the coroutine
162
+ $this->nextCoroutine($nextYield);
163
+ } catch (Exception $exception) {
164
+ $this->result->reject($exception);
165
+ } catch (Throwable $throwable) {
166
+ $this->result->reject($throwable);
167
+ }
168
+ }
169
+ }
src/vendor/guzzlehttp/promises/src/Create.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ final class Create
6
+ {
7
+ /**
8
+ * Creates a promise for a value if the value is not a promise.
9
+ *
10
+ * @param mixed $value Promise or value.
11
+ *
12
+ * @return PromiseInterface
13
+ */
14
+ public static function promiseFor($value)
15
+ {
16
+ if ($value instanceof PromiseInterface) {
17
+ return $value;
18
+ }
19
+
20
+ // Return a Guzzle promise that shadows the given promise.
21
+ if (is_object($value) && method_exists($value, 'then')) {
22
+ $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
23
+ $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
24
+ $promise = new Promise($wfn, $cfn);
25
+ $value->then([$promise, 'resolve'], [$promise, 'reject']);
26
+ return $promise;
27
+ }
28
+
29
+ return new FulfilledPromise($value);
30
+ }
31
+
32
+ /**
33
+ * Creates a rejected promise for a reason if the reason is not a promise.
34
+ * If the provided reason is a promise, then it is returned as-is.
35
+ *
36
+ * @param mixed $reason Promise or reason.
37
+ *
38
+ * @return PromiseInterface
39
+ */
40
+ public static function rejectionFor($reason)
41
+ {
42
+ if ($reason instanceof PromiseInterface) {
43
+ return $reason;
44
+ }
45
+
46
+ return new RejectedPromise($reason);
47
+ }
48
+
49
+ /**
50
+ * Create an exception for a rejected promise value.
51
+ *
52
+ * @param mixed $reason
53
+ *
54
+ * @return \Exception|\Throwable
55
+ */
56
+ public static function exceptionFor($reason)
57
+ {
58
+ if ($reason instanceof \Exception || $reason instanceof \Throwable) {
59
+ return $reason;
60
+ }
61
+
62
+ return new RejectionException($reason);
63
+ }
64
+
65
+ /**
66
+ * Returns an iterator for the given value.
67
+ *
68
+ * @param mixed $value
69
+ *
70
+ * @return \Iterator
71
+ */
72
+ public static function iterFor($value)
73
+ {
74
+ if ($value instanceof \Iterator) {
75
+ return $value;
76
+ }
77
+
78
+ if (is_array($value)) {
79
+ return new \ArrayIterator($value);
80
+ }
81
+
82
+ return new \ArrayIterator([$value]);
83
+ }
84
+ }
src/vendor/guzzlehttp/promises/src/Each.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ final class Each
6
+ {
7
+ /**
8
+ * Given an iterator that yields promises or values, returns a promise that
9
+ * is fulfilled with a null value when the iterator has been consumed or
10
+ * the aggregate promise has been fulfilled or rejected.
11
+ *
12
+ * $onFulfilled is a function that accepts the fulfilled value, iterator
13
+ * index, and the aggregate promise. The callback can invoke any necessary
14
+ * side effects and choose to resolve or reject the aggregate if needed.
15
+ *
16
+ * $onRejected is a function that accepts the rejection reason, iterator
17
+ * index, and the aggregate promise. The callback can invoke any necessary
18
+ * side effects and choose to resolve or reject the aggregate if needed.
19
+ *
20
+ * @param mixed $iterable Iterator or array to iterate over.
21
+ * @param callable $onFulfilled
22
+ * @param callable $onRejected
23
+ *
24
+ * @return PromiseInterface
25
+ */
26
+ public static function of(
27
+ $iterable,
28
+ callable $onFulfilled = null,
29
+ callable $onRejected = null
30
+ ) {
31
+ return (new EachPromise($iterable, [
32
+ 'fulfilled' => $onFulfilled,
33
+ 'rejected' => $onRejected
34
+ ]))->promise();
35
+ }
36
+
37
+ /**
38
+ * Like of, but only allows a certain number of outstanding promises at any
39
+ * given time.
40
+ *
41
+ * $concurrency may be an integer or a function that accepts the number of
42
+ * pending promises and returns a numeric concurrency limit value to allow
43
+ * for dynamic a concurrency size.
44
+ *
45
+ * @param mixed $iterable
46
+ * @param int|callable $concurrency
47
+ * @param callable $onFulfilled
48
+ * @param callable $onRejected
49
+ *
50
+ * @return PromiseInterface
51
+ */
52
+ public static function ofLimit(
53
+ $iterable,
54
+ $concurrency,
55
+ callable $onFulfilled = null,
56
+ callable $onRejected = null
57
+ ) {
58
+ return (new EachPromise($iterable, [
59
+ 'fulfilled' => $onFulfilled,
60
+ 'rejected' => $onRejected,
61
+ 'concurrency' => $concurrency
62
+ ]))->promise();
63
+ }
64
+
65
+ /**
66
+ * Like limit, but ensures that no promise in the given $iterable argument
67
+ * is rejected. If any promise is rejected, then the aggregate promise is
68
+ * rejected with the encountered rejection.
69
+ *
70
+ * @param mixed $iterable
71
+ * @param int|callable $concurrency
72
+ * @param callable $onFulfilled
73
+ *
74
+ * @return PromiseInterface
75
+ */
76
+ public static function ofLimitAll(
77
+ $iterable,
78
+ $concurrency,
79
+ callable $onFulfilled = null
80
+ ) {
81
+ return each_limit(
82
+ $iterable,
83
+ $concurrency,
84
+ $onFulfilled,
85
+ function ($reason, $idx, PromiseInterface $aggregate) {
86
+ $aggregate->reject($reason);
87
+ }
88
+ );
89
+ }
90
+ }
src/vendor/guzzlehttp/promises/src/EachPromise.php ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * Represents a promise that iterates over many promises and invokes
7
+ * side-effect functions in the process.
8
+ */
9
+ class EachPromise implements PromisorInterface
10
+ {
11
+ private $pending = [];
12
+
13
+ private $nextPendingIndex = 0;
14
+
15
+ /** @var \Iterator|null */
16
+ private $iterable;
17
+
18
+ /** @var callable|int|null */
19
+ private $concurrency;
20
+
21
+ /** @var callable|null */
22
+ private $onFulfilled;
23
+
24
+ /** @var callable|null */
25
+ private $onRejected;
26
+
27
+ /** @var Promise|null */
28
+ private $aggregate;
29
+
30
+ /** @var bool|null */
31
+ private $mutex;
32
+
33
+ /**
34
+ * Configuration hash can include the following key value pairs:
35
+ *
36
+ * - fulfilled: (callable) Invoked when a promise fulfills. The function
37
+ * is invoked with three arguments: the fulfillment value, the index
38
+ * position from the iterable list of the promise, and the aggregate
39
+ * promise that manages all of the promises. The aggregate promise may
40
+ * be resolved from within the callback to short-circuit the promise.
41
+ * - rejected: (callable) Invoked when a promise is rejected. The
42
+ * function is invoked with three arguments: the rejection reason, the
43
+ * index position from the iterable list of the promise, and the
44
+ * aggregate promise that manages all of the promises. The aggregate
45
+ * promise may be resolved from within the callback to short-circuit
46
+ * the promise.
47
+ * - concurrency: (integer) Pass this configuration option to limit the
48
+ * allowed number of outstanding concurrently executing promises,
49
+ * creating a capped pool of promises. There is no limit by default.
50
+ *
51
+ * @param mixed $iterable Promises or values to iterate.
52
+ * @param array $config Configuration options
53
+ */
54
+ public function __construct($iterable, array $config = [])
55
+ {
56
+ $this->iterable = Create::iterFor($iterable);
57
+
58
+ if (isset($config['concurrency'])) {
59
+ $this->concurrency = $config['concurrency'];
60
+ }
61
+
62
+ if (isset($config['fulfilled'])) {
63
+ $this->onFulfilled = $config['fulfilled'];
64
+ }
65
+
66
+ if (isset($config['rejected'])) {
67
+ $this->onRejected = $config['rejected'];
68
+ }
69
+ }
70
+
71
+ /** @psalm-suppress InvalidNullableReturnType */
72
+ public function promise()
73
+ {
74
+ if ($this->aggregate) {
75
+ return $this->aggregate;
76
+ }
77
+
78
+ try {
79
+ $this->createPromise();
80
+ /** @psalm-assert Promise $this->aggregate */
81
+ $this->iterable->rewind();
82
+ $this->refillPending();
83
+ } catch (\Throwable $e) {
84
+ /**
85
+ * @psalm-suppress NullReference
86
+ * @phpstan-ignore-next-line
87
+ */
88
+ $this->aggregate->reject($e);
89
+ } catch (\Exception $e) {
90
+ /**
91
+ * @psalm-suppress NullReference
92
+ * @phpstan-ignore-next-line
93
+ */
94
+ $this->aggregate->reject($e);
95
+ }
96
+
97
+ /**
98
+ * @psalm-suppress NullableReturnStatement
99
+ * @phpstan-ignore-next-line
100
+ */
101
+ return $this->aggregate;
102
+ }
103
+
104
+ private function createPromise()
105
+ {
106
+ $this->mutex = false;
107
+ $this->aggregate = new Promise(function () {
108
+ if ($this->checkIfFinished()) {
109
+ return;
110
+ }
111
+ reset($this->pending);
112
+ // Consume a potentially fluctuating list of promises while
113
+ // ensuring that indexes are maintained (precluding array_shift).
114
+ while ($promise = current($this->pending)) {
115
+ next($this->pending);
116
+ $promise->wait();
117
+ if (Is::settled($this->aggregate)) {
118
+ return;
119
+ }
120
+ }
121
+ });
122
+
123
+ // Clear the references when the promise is resolved.
124
+ $clearFn = function () {
125
+ $this->iterable = $this->concurrency = $this->pending = null;
126
+ $this->onFulfilled = $this->onRejected = null;
127
+ $this->nextPendingIndex = 0;
128
+ };
129
+
130
+ $this->aggregate->then($clearFn, $clearFn);
131
+ }
132
+
133
+ private function refillPending()
134
+ {
135
+ if (!$this->concurrency) {
136
+ // Add all pending promises.
137
+ while ($this->addPending() && $this->advanceIterator());
138
+ return;
139
+ }
140
+
141
+ // Add only up to N pending promises.
142
+ $concurrency = is_callable($this->concurrency)
143
+ ? call_user_func($this->concurrency, count($this->pending))
144
+ : $this->concurrency;
145
+ $concurrency = max($concurrency - count($this->pending), 0);
146
+ // Concurrency may be set to 0 to disallow new promises.
147
+ if (!$concurrency) {
148
+ return;
149
+ }
150
+ // Add the first pending promise.
151
+ $this->addPending();
152
+ // Note this is special handling for concurrency=1 so that we do
153
+ // not advance the iterator after adding the first promise. This
154
+ // helps work around issues with generators that might not have the
155
+ // next value to yield until promise callbacks are called.
156
+ while (--$concurrency
157
+ && $this->advanceIterator()
158
+ && $this->addPending());
159
+ }
160
+
161
+ private function addPending()
162
+ {
163
+ if (!$this->iterable || !$this->iterable->valid()) {
164
+ return false;
165
+ }
166
+
167
+ $promise = Create::promiseFor($this->iterable->current());
168
+ $key = $this->iterable->key();
169
+
170
+ // Iterable keys may not be unique, so we use a counter to
171
+ // guarantee uniqueness
172
+ $idx = $this->nextPendingIndex++;
173
+
174
+ $this->pending[$idx] = $promise->then(
175
+ function ($value) use ($idx, $key) {
176
+ if ($this->onFulfilled) {
177
+ call_user_func(
178
+ $this->onFulfilled,
179
+ $value,
180
+ $key,
181
+ $this->aggregate
182
+ );
183
+ }
184
+ $this->step($idx);
185
+ },
186
+ function ($reason) use ($idx, $key) {
187
+ if ($this->onRejected) {
188
+ call_user_func(
189
+ $this->onRejected,
190
+ $reason,
191
+ $key,
192
+ $this->aggregate
193
+ );
194
+ }
195
+ $this->step($idx);
196
+ }
197
+ );
198
+
199
+ return true;
200
+ }
201
+
202
+ private function advanceIterator()
203
+ {
204
+ // Place a lock on the iterator so that we ensure to not recurse,
205
+ // preventing fatal generator errors.
206
+ if ($this->mutex) {
207
+ return false;
208
+ }
209
+
210
+ $this->mutex = true;
211
+
212
+ try {
213
+ $this->iterable->next();
214
+ $this->mutex = false;
215
+ return true;
216
+ } catch (\Throwable $e) {
217
+ $this->aggregate->reject($e);
218
+ $this->mutex = false;
219
+ return false;
220
+ } catch (\Exception $e) {
221
+ $this->aggregate->reject($e);
222
+ $this->mutex = false;
223
+ return false;
224
+ }
225
+ }
226
+
227
+ private function step($idx)
228
+ {
229
+ // If the promise was already resolved, then ignore this step.
230
+ if (Is::settled($this->aggregate)) {
231
+ return;
232
+ }
233
+
234
+ unset($this->pending[$idx]);
235
+
236
+ // Only refill pending promises if we are not locked, preventing the
237
+ // EachPromise to recursively invoke the provided iterator, which
238
+ // cause a fatal error: "Cannot resume an already running generator"
239
+ if ($this->advanceIterator() && !$this->checkIfFinished()) {
240
+ // Add more pending promises if possible.
241
+ $this->refillPending();
242
+ }
243
+ }
244
+
245
+ private function checkIfFinished()
246
+ {
247
+ if (!$this->pending && !$this->iterable->valid()) {
248
+ // Resolve the promise if there's nothing left to do.
249
+ $this->aggregate->resolve(null);
250
+ return true;
251
+ }
252
+
253
+ return false;
254
+ }
255
+ }
src/vendor/guzzlehttp/promises/src/FulfilledPromise.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * A promise that has been fulfilled.
7
+ *
8
+ * Thenning off of this promise will invoke the onFulfilled callback
9
+ * immediately and ignore other callbacks.
10
+ */
11
+ class FulfilledPromise implements PromiseInterface
12
+ {
13
+ private $value;
14
+
15
+ public function __construct($value)
16
+ {
17
+ if (is_object($value) && method_exists($value, 'then')) {
18
+ throw new \InvalidArgumentException(
19
+ 'You cannot create a FulfilledPromise with a promise.'
20
+ );
21
+ }
22
+
23
+ $this->value = $value;
24
+ }
25
+
26
+ public function then(
27
+ callable $onFulfilled = null,
28
+ callable $onRejected = null
29
+ ) {
30
+ // Return itself if there is no onFulfilled function.
31
+ if (!$onFulfilled) {
32
+ return $this;
33
+ }
34
+
35
+ $queue = Utils::queue();
36
+ $p = new Promise([$queue, 'run']);
37
+ $value = $this->value;
38
+ $queue->add(static function () use ($p, $value, $onFulfilled) {
39
+ if (Is::pending($p)) {
40
+ try {
41
+ $p->resolve($onFulfilled($value));
42
+ } catch (\Throwable $e) {
43
+ $p->reject($e);
44
+ } catch (\Exception $e) {
45
+ $p->reject($e);
46
+ }
47
+ }
48
+ });
49
+
50
+ return $p;
51
+ }
52
+
53
+ public function otherwise(callable $onRejected)
54
+ {
55
+ return $this->then(null, $onRejected);
56
+ }
57
+
58
+ public function wait($unwrap = true, $defaultDelivery = null)
59
+ {
60
+ return $unwrap ? $this->value : null;
61
+ }
62
+
63
+ public function getState()
64
+ {
65
+ return self::FULFILLED;
66
+ }
67
+
68
+ public function resolve($value)
69
+ {
70
+ if ($value !== $this->value) {
71
+ throw new \LogicException("Cannot resolve a fulfilled promise");
72
+ }
73
+ }
74
+
75
+ public function reject($reason)
76
+ {
77
+ throw new \LogicException("Cannot reject a fulfilled promise");
78
+ }
79
+
80
+ public function cancel()
81
+ {
82
+ // pass
83
+ }
84
+ }
src/vendor/guzzlehttp/promises/src/Is.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ final class Is
6
+ {
7
+ /**
8
+ * Returns true if a promise is pending.
9
+ *
10
+ * @return bool
11
+ */
12
+ public static function pending(PromiseInterface $promise)
13
+ {
14
+ return $promise->getState() === PromiseInterface::PENDING;
15
+ }
16
+
17
+ /**
18
+ * Returns true if a promise is fulfilled or rejected.
19
+ *
20
+ * @return bool
21
+ */
22
+ public static function settled(PromiseInterface $promise)
23
+ {
24
+ return $promise->getState() !== PromiseInterface::PENDING;
25
+ }
26
+
27
+ /**
28
+ * Returns true if a promise is fulfilled.
29
+ *
30
+ * @return bool
31
+ */
32
+ public static function fulfilled(PromiseInterface $promise)
33
+ {
34
+ return $promise->getState() === PromiseInterface::FULFILLED;
35
+ }
36
+
37
+ /**
38
+ * Returns true if a promise is rejected.
39
+ *
40
+ * @return bool
41
+ */
42
+ public static function rejected(PromiseInterface $promise)
43
+ {
44
+ return $promise->getState() === PromiseInterface::REJECTED;
45
+ }
46
+ }
src/vendor/guzzlehttp/promises/src/Promise.php ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * Promises/A+ implementation that avoids recursion when possible.
7
+ *
8
+ * @link https://promisesaplus.com/
9
+ */
10
+ class Promise implements PromiseInterface
11
+ {
12
+ private $state = self::PENDING;
13
+ private $result;
14
+ private $cancelFn;
15
+ private $waitFn;
16
+ private $waitList;
17
+ private $handlers = [];
18
+
19
+ /**
20
+ * @param callable $waitFn Fn that when invoked resolves the promise.
21
+ * @param callable $cancelFn Fn that when invoked cancels the promise.
22
+ */
23
+ public function __construct(
24
+ callable $waitFn = null,
25
+ callable $cancelFn = null
26
+ ) {
27
+ $this->waitFn = $waitFn;
28
+ $this->cancelFn = $cancelFn;
29
+ }
30
+
31
+ public function then(
32
+ callable $onFulfilled = null,
33
+ callable $onRejected = null
34
+ ) {
35
+ if ($this->state === self::PENDING) {
36
+ $p = new Promise(null, [$this, 'cancel']);
37
+ $this->handlers[] = [$p, $onFulfilled, $onRejected];
38
+ $p->waitList = $this->waitList;
39
+ $p->waitList[] = $this;
40
+ return $p;
41
+ }
42
+
43
+ // Return a fulfilled promise and immediately invoke any callbacks.
44
+ if ($this->state === self::FULFILLED) {
45
+ $promise = Create::promiseFor($this->result);
46
+ return $onFulfilled ? $promise->then($onFulfilled) : $promise;
47
+ }
48
+
49
+ // It's either cancelled or rejected, so return a rejected promise
50
+ // and immediately invoke any callbacks.
51
+ $rejection = Create::rejectionFor($this->result);
52
+ return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
53
+ }
54
+
55
+ public function otherwise(callable $onRejected)
56
+ {
57
+ return $this->then(null, $onRejected);
58
+ }
59
+
60
+ public function wait($unwrap = true)
61
+ {
62
+ $this->waitIfPending();
63
+
64
+ if ($this->result instanceof PromiseInterface) {
65
+ return $this->result->wait($unwrap);
66
+ }
67
+ if ($unwrap) {
68
+ if ($this->state === self::FULFILLED) {
69
+ return $this->result;
70
+ }
71
+ // It's rejected so "unwrap" and throw an exception.
72
+ throw Create::exceptionFor($this->result);
73
+ }
74
+ }
75
+
76
+ public function getState()
77
+ {
78
+ return $this->state;
79
+ }
80
+
81
+ public function cancel()
82
+ {
83
+ if ($this->state !== self::PENDING) {
84
+ return;
85
+ }
86
+
87
+ $this->waitFn = $this->waitList = null;
88
+
89
+ if ($this->cancelFn) {
90
+ $fn = $this->cancelFn;
91
+ $this->cancelFn = null;
92
+ try {
93
+ $fn();
94
+ } catch (\Throwable $e) {
95
+ $this->reject($e);
96
+ } catch (\Exception $e) {
97
+ $this->reject($e);
98
+ }
99
+ }
100
+
101
+ // Reject the promise only if it wasn't rejected in a then callback.
102
+ /** @psalm-suppress RedundantCondition */
103
+ if ($this->state === self::PENDING) {
104
+ $this->reject(new CancellationException('Promise has been cancelled'));
105
+ }
106
+ }
107
+
108
+ public function resolve($value)
109
+ {
110
+ $this->settle(self::FULFILLED, $value);
111
+ }
112
+
113
+ public function reject($reason)
114
+ {
115
+ $this->settle(self::REJECTED, $reason);
116
+ }
117
+
118
+ private function settle($state, $value)
119
+ {
120
+ if ($this->state !== self::PENDING) {
121
+ // Ignore calls with the same resolution.
122
+ if ($state === $this->state && $value === $this->result) {
123
+ return;
124
+ }
125
+ throw $this->state === $state
126
+ ? new \LogicException("The promise is already {$state}.")
127
+ : new \LogicException("Cannot change a {$this->state} promise to {$state}");
128
+ }
129
+
130
+ if ($value === $this) {
131
+ throw new \LogicException('Cannot fulfill or reject a promise with itself');
132
+ }
133
+
134
+ // Clear out the state of the promise but stash the handlers.
135
+ $this->state = $state;
136
+ $this->result = $value;
137
+ $handlers = $this->handlers;
138
+ $this->handlers = null;
139
+ $this->waitList = $this->waitFn = null;
140
+ $this->cancelFn = null;
141
+
142
+ if (!$handlers) {
143
+ return;
144
+ }
145
+
146
+ // If the value was not a settled promise or a thenable, then resolve
147
+ // it in the task queue using the correct ID.
148
+ if (!is_object($value) || !method_exists($value, 'then')) {
149
+ $id = $state === self::FULFILLED ? 1 : 2;
150
+ // It's a success, so resolve the handlers in the queue.
151
+ Utils::queue()->add(static function () use ($id, $value, $handlers) {
152
+ foreach ($handlers as $handler) {
153
+ self::callHandler($id, $value, $handler);
154
+ }
155
+ });
156
+ } elseif ($value instanceof Promise && Is::pending($value)) {
157
+ // We can just merge our handlers onto the next promise.
158
+ $value->handlers = array_merge($value->handlers, $handlers);
159
+ } else {
160
+ // Resolve the handlers when the forwarded promise is resolved.
161
+ $value->then(
162
+ static function ($value) use ($handlers) {
163
+ foreach ($handlers as $handler) {
164
+ self::callHandler(1, $value, $handler);
165
+ }
166
+ },
167
+ static function ($reason) use ($handlers) {
168
+ foreach ($handlers as $handler) {
169
+ self::callHandler(2, $reason, $handler);
170
+ }
171
+ }
172
+ );
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Call a stack of handlers using a specific callback index and value.
178
+ *
179
+ * @param int $index 1 (resolve) or 2 (reject).
180
+ * @param mixed $value Value to pass to the callback.
181
+ * @param array $handler Array of handler data (promise and callbacks).
182
+ */
183
+ private static function callHandler($index, $value, array $handler)
184
+ {
185
+ /** @var PromiseInterface $promise */
186
+ $promise = $handler[0];
187
+
188
+ // The promise may have been cancelled or resolved before placing
189
+ // this thunk in the queue.
190
+ if (Is::settled($promise)) {
191
+ return;
192
+ }
193
+
194
+ try {
195
+ if (isset($handler[$index])) {
196
+ /*
197
+ * If $f throws an exception, then $handler will be in the exception
198
+ * stack trace. Since $handler contains a reference to the callable
199
+ * itself we get a circular reference. We clear the $handler
200
+ * here to avoid that memory leak.
201
+ */
202
+ $f = $handler[$index];
203
+ unset($handler);
204
+ $promise->resolve($f($value));
205
+ } elseif ($index === 1) {
206
+ // Forward resolution values as-is.
207
+ $promise->resolve($value);
208
+ } else {
209
+ // Forward rejections down the chain.
210
+ $promise->reject($value);
211
+ }
212
+ } catch (\Throwable $reason) {
213
+ $promise->reject($reason);
214
+ } catch (\Exception $reason) {
215
+ $promise->reject($reason);
216
+ }
217
+ }
218
+
219
+ private function waitIfPending()
220
+ {
221
+ if ($this->state !== self::PENDING) {
222
+ return;
223
+ } elseif ($this->waitFn) {
224
+ $this->invokeWaitFn();
225
+ } elseif ($this->waitList) {
226
+ $this->invokeWaitList();
227
+ } else {
228
+ // If there's no wait function, then reject the promise.
229
+ $this->reject('Cannot wait on a promise that has '
230
+ . 'no internal wait function. You must provide a wait '
231
+ . 'function when constructing the promise to be able to '
232
+ . 'wait on a promise.');
233
+ }
234
+
235
+ Utils::queue()->run();
236
+
237
+ /** @psalm-suppress RedundantCondition */
238
+ if ($this->state === self::PENDING) {
239
+ $this->reject('Invoking the wait callback did not resolve the promise');
240
+ }
241
+ }
242
+
243
+ private function invokeWaitFn()
244
+ {
245
+ try {
246
+ $wfn = $this->waitFn;
247
+ $this->waitFn = null;
248
+ $wfn(true);
249
+ } catch (\Exception $reason) {
250
+ if ($this->state === self::PENDING) {
251
+ // The promise has not been resolved yet, so reject the promise
252
+ // with the exception.
253
+ $this->reject($reason);
254
+ } else {
255
+ // The promise was already resolved, so there's a problem in
256
+ // the application.
257
+ throw $reason;
258
+ }
259
+ }
260
+ }
261
+
262
+ private function invokeWaitList()
263
+ {
264
+ $waitList = $this->waitList;
265
+ $this->waitList = null;
266
+
267
+ foreach ($waitList as $result) {
268
+ do {
269
+ $result->waitIfPending();
270
+ $result = $result->result;
271
+ } while ($result instanceof Promise);
272
+
273
+ if ($result instanceof PromiseInterface) {
274
+ $result->wait(false);
275
+ }
276
+ }
277
+ }
278
+ }
src/vendor/guzzlehttp/promises/src/PromiseInterface.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * A promise represents the eventual result of an asynchronous operation.
7
+ *
8
+ * The primary way of interacting with a promise is through its then method,
9
+ * which registers callbacks to receive either a promise’s eventual value or
10
+ * the reason why the promise cannot be fulfilled.
11
+ *
12
+ * @link https://promisesaplus.com/
13
+ */
14
+ interface PromiseInterface
15
+ {
16
+ const PENDING = 'pending';
17
+ const FULFILLED = 'fulfilled';
18
+ const REJECTED = 'rejected';
19
+
20
+ /**
21
+ * Appends fulfillment and rejection handlers to the promise, and returns
22
+ * a new promise resolving to the return value of the called handler.
23
+ *
24
+ * @param callable $onFulfilled Invoked when the promise fulfills.
25
+ * @param callable $onRejected Invoked when the promise is rejected.
26
+ *
27
+ * @return PromiseInterface
28
+ */
29
+ public function then(
30
+ callable $onFulfilled = null,
31
+ callable $onRejected = null
32
+ );
33
+
34
+ /**
35
+ * Appends a rejection handler callback to the promise, and returns a new
36
+ * promise resolving to the return value of the callback if it is called,
37
+ * or to its original fulfillment value if the promise is instead
38
+ * fulfilled.
39
+ *
40
+ * @param callable $onRejected Invoked when the promise is rejected.
41
+ *
42
+ * @return PromiseInterface
43
+ */
44
+ public function otherwise(callable $onRejected);
45
+
46
+ /**
47
+ * Get the state of the promise ("pending", "rejected", or "fulfilled").
48
+ *
49
+ * The three states can be checked against the constants defined on
50
+ * PromiseInterface: PENDING, FULFILLED, and REJECTED.
51
+ *
52
+ * @return string
53
+ */
54
+ public function getState();
55
+
56
+ /**
57
+ * Resolve the promise with the given value.
58
+ *
59
+ * @param mixed $value
60
+ *
61
+ * @throws \RuntimeException if the promise is already resolved.
62
+ */
63
+ public function resolve($value);
64
+
65
+ /**
66
+ * Reject the promise with the given reason.
67
+ *
68
+ * @param mixed $reason
69
+ *
70
+ * @throws \RuntimeException if the promise is already resolved.
71
+ */
72
+ public function reject($reason);
73
+
74
+ /**
75
+ * Cancels the promise if possible.
76
+ *
77
+ * @link https://github.com/promises-aplus/cancellation-spec/issues/7
78
+ */
79
+ public function cancel();
80
+
81
+ /**
82
+ * Waits until the promise completes if possible.
83
+ *
84
+ * Pass $unwrap as true to unwrap the result of the promise, either
85
+ * returning the resolved value or throwing the rejected exception.
86
+ *
87
+ * If the promise cannot be waited on, then the promise will be rejected.
88
+ *
89
+ * @param bool $unwrap
90
+ *
91
+ * @return mixed
92
+ *
93
+ * @throws \LogicException if the promise has no wait function or if the
94
+ * promise does not settle after waiting.
95
+ */
96
+ public function wait($unwrap = true);
97
+ }
src/vendor/guzzlehttp/promises/src/PromisorInterface.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * Interface used with classes that return a promise.
7
+ */
8
+ interface PromisorInterface
9
+ {
10
+ /**
11
+ * Returns a promise.
12
+ *
13
+ * @return PromiseInterface
14
+ */
15
+ public function promise();
16
+ }
src/vendor/guzzlehttp/promises/src/RejectedPromise.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * A promise that has been rejected.
7
+ *
8
+ * Thenning off of this promise will invoke the onRejected callback
9
+ * immediately and ignore other callbacks.
10
+ */
11
+ class RejectedPromise implements PromiseInterface
12
+ {
13
+ private $reason;
14
+
15
+ public function __construct($reason)
16
+ {
17
+ if (is_object($reason) && method_exists($reason, 'then')) {
18
+ throw new \InvalidArgumentException(
19
+ 'You cannot create a RejectedPromise with a promise.'
20
+ );
21
+ }
22
+
23
+ $this->reason = $reason;
24
+ }
25
+
26
+ public function then(
27
+ callable $onFulfilled = null,
28
+ callable $onRejected = null
29
+ ) {
30
+ // If there's no onRejected callback then just return self.
31
+ if (!$onRejected) {
32
+ return $this;
33
+ }
34
+
35
+ $queue = Utils::queue();
36
+ $reason = $this->reason;
37
+ $p = new Promise([$queue, 'run']);
38
+ $queue->add(static function () use ($p, $reason, $onRejected) {
39
+ if (Is::pending($p)) {
40
+ try {
41
+ // Return a resolved promise if onRejected does not throw.
42
+ $p->resolve($onRejected($reason));
43
+ } catch (\Throwable $e) {
44
+ // onRejected threw, so return a rejected promise.
45
+ $p->reject($e);
46
+ } catch (\Exception $e) {
47
+ // onRejected threw, so return a rejected promise.
48
+ $p->reject($e);
49
+ }
50
+ }
51
+ });
52
+
53
+ return $p;
54
+ }
55
+
56
+ public function otherwise(callable $onRejected)
57
+ {
58
+ return $this->then(null, $onRejected);
59
+ }
60
+
61
+ public function wait($unwrap = true, $defaultDelivery = null)
62
+ {
63
+ if ($unwrap) {
64
+ throw Create::exceptionFor($this->reason);
65
+ }
66
+
67
+ return null;
68
+ }
69
+
70
+ public function getState()
71
+ {
72
+ return self::REJECTED;
73
+ }
74
+
75
+ public function resolve($value)
76
+ {
77
+ throw new \LogicException("Cannot resolve a rejected promise");
78
+ }
79
+
80
+ public function reject($reason)
81
+ {
82
+ if ($reason !== $this->reason) {
83
+ throw new \LogicException("Cannot reject a rejected promise");
84
+ }
85
+ }
86
+
87
+ public function cancel()
88
+ {
89
+ // pass
90
+ }
91
+ }
src/vendor/guzzlehttp/promises/src/RejectionException.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * A special exception that is thrown when waiting on a rejected promise.
7
+ *
8
+ * The reason value is available via the getReason() method.
9
+ */
10
+ class RejectionException extends \RuntimeException
11
+ {
12
+ /** @var mixed Rejection reason. */
13
+ private $reason;
14
+
15
+ /**
16
+ * @param mixed $reason Rejection reason.
17
+ * @param string $description Optional description
18
+ */
19
+ public function __construct($reason, $description = null)
20
+ {
21
+ $this->reason = $reason;
22
+
23
+ $message = 'The promise was rejected';
24
+
25
+ if ($description) {
26
+ $message .= ' with reason: ' . $description;
27
+ } elseif (is_string($reason)
28
+ || (is_object($reason) && method_exists($reason, '__toString'))
29
+ ) {
30
+ $message .= ' with reason: ' . $this->reason;
31
+ } elseif ($reason instanceof \JsonSerializable) {
32
+ $message .= ' with reason: '
33
+ . json_encode($this->reason, JSON_PRETTY_PRINT);
34
+ }
35
+
36
+ parent::__construct($message);
37
+ }
38
+
39
+ /**
40
+ * Returns the rejection reason.
41
+ *
42
+ * @return mixed
43
+ */
44
+ public function getReason()
45
+ {
46
+ return $this->reason;
47
+ }
48
+ }
src/vendor/guzzlehttp/promises/src/TaskQueue.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * A task queue that executes tasks in a FIFO order.
7
+ *
8
+ * This task queue class is used to settle promises asynchronously and
9
+ * maintains a constant stack size. You can use the task queue asynchronously
10
+ * by calling the `run()` function of the global task queue in an event loop.
11
+ *
12
+ * GuzzleHttp\Promise\Utils::queue()->run();
13
+ */
14
+ class TaskQueue implements TaskQueueInterface
15
+ {
16
+ private $enableShutdown = true;
17
+ private $queue = [];
18
+
19
+ public function __construct($withShutdown = true)
20
+ {
21
+ if ($withShutdown) {
22
+ register_shutdown_function(function () {
23
+ if ($this->enableShutdown) {
24
+ // Only run the tasks if an E_ERROR didn't occur.
25
+ $err = error_get_last();
26
+ if (!$err || ($err['type'] ^ E_ERROR)) {
27
+ $this->run();
28
+ }
29
+ }
30
+ });
31
+ }
32
+ }
33
+
34
+ public function isEmpty()
35
+ {
36
+ return !$this->queue;
37
+ }
38
+
39
+ public function add(callable $task)
40
+ {
41
+ $this->queue[] = $task;
42
+ }
43
+
44
+ public function run()
45
+ {
46
+ while ($task = array_shift($this->queue)) {
47
+ /** @var callable $task */
48
+ $task();
49
+ }
50
+ }
51
+
52
+ /**
53
+ * The task queue will be run and exhausted by default when the process
54
+ * exits IFF the exit is not the result of a PHP E_ERROR error.
55
+ *
56
+ * You can disable running the automatic shutdown of the queue by calling
57
+ * this function. If you disable the task queue shutdown process, then you
58
+ * MUST either run the task queue (as a result of running your event loop
59
+ * or manually using the run() method) or wait on each outstanding promise.
60
+ *
61
+ * Note: This shutdown will occur before any destructors are triggered.
62
+ */
63
+ public function disableShutdown()
64
+ {
65
+ $this->enableShutdown = false;
66
+ }
67
+ }
src/vendor/guzzlehttp/promises/src/TaskQueueInterface.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ interface TaskQueueInterface
6
+ {
7
+ /**
8
+ * Returns true if the queue is empty.
9
+ *
10
+ * @return bool
11
+ */
12
+ public function isEmpty();
13
+
14
+ /**
15
+ * Adds a task to the queue that will be executed the next time run is
16
+ * called.
17
+ */
18
+ public function add(callable $task);
19
+
20
+ /**
21
+ * Execute all of the pending task in the queue.
22
+ */
23
+ public function run();
24
+ }
src/vendor/guzzlehttp/promises/src/Utils.php ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ final class Utils
6
+ {
7
+ /**
8
+ * Get the global task queue used for promise resolution.
9
+ *
10
+ * This task queue MUST be run in an event loop in order for promises to be
11
+ * settled asynchronously. It will be automatically run when synchronously
12
+ * waiting on a promise.
13
+ *
14
+ * <code>
15
+ * while ($eventLoop->isRunning()) {
16
+ * GuzzleHttp\Promise\Utils::queue()->run();
17
+ * }
18
+ * </code>
19
+ *
20
+ * @param TaskQueueInterface $assign Optionally specify a new queue instance.
21
+ *
22
+ * @return TaskQueueInterface
23
+ */
24
+ public static function queue(TaskQueueInterface $assign = null)
25
+ {
26
+ static $queue;
27
+
28
+ if ($assign) {
29
+ $queue = $assign;
30
+ } elseif (!$queue) {
31
+ $queue = new TaskQueue();
32
+ }
33
+
34
+ return $queue;
35
+ }
36
+
37
+ /**
38
+ * Adds a function to run in the task queue when it is next `run()` and
39
+ * returns a promise that is fulfilled or rejected with the result.
40
+ *
41
+ * @param callable $task Task function to run.
42
+ *
43
+ * @return PromiseInterface
44
+ */
45
+ public static function task(callable $task)
46
+ {
47
+ $queue = self::queue();
48
+ $promise = new Promise([$queue, 'run']);
49
+ $queue->add(function () use ($task, $promise) {
50
+ try {
51
+ if (Is::pending($promise)) {
52
+ $promise->resolve($task());
53
+ }
54
+ } catch (\Throwable $e) {
55
+ $promise->reject($e);
56
+ } catch (\Exception $e) {
57
+ $promise->reject($e);
58
+ }
59
+ });
60
+
61
+ return $promise;
62
+ }
63
+
64
+ /**
65
+ * Synchronously waits on a promise to resolve and returns an inspection
66
+ * state array.
67
+ *
68
+ * Returns a state associative array containing a "state" key mapping to a
69
+ * valid promise state. If the state of the promise is "fulfilled", the
70
+ * array will contain a "value" key mapping to the fulfilled value of the
71
+ * promise. If the promise is rejected, the array will contain a "reason"
72
+ * key mapping to the rejection reason of the promise.
73
+ *
74
+ * @param PromiseInterface $promise Promise or value.
75
+ *
76
+ * @return array
77
+ */
78
+ public static function inspect(PromiseInterface $promise)
79
+ {
80
+ try {
81
+ return [
82
+ 'state' => PromiseInterface::FULFILLED,
83
+ 'value' => $promise->wait()
84
+ ];
85
+ } catch (RejectionException $e) {
86
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
87
+ } catch (\Throwable $e) {
88
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
89
+ } catch (\Exception $e) {
90
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Waits on all of the provided promises, but does not unwrap rejected
96
+ * promises as thrown exception.
97
+ *
98
+ * Returns an array of inspection state arrays.
99
+ *
100
+ * @see inspect for the inspection state array format.
101
+ *
102
+ * @param PromiseInterface[] $promises Traversable of promises to wait upon.
103
+ *
104
+ * @return array
105
+ */
106
+ public static function inspectAll($promises)
107
+ {
108
+ $results = [];
109
+ foreach ($promises as $key => $promise) {
110
+ $results[$key] = inspect($promise);
111
+ }
112
+
113
+ return $results;
114
+ }
115
+
116
+ /**
117
+ * Waits on all of the provided promises and returns the fulfilled values.
118
+ *
119
+ * Returns an array that contains the value of each promise (in the same
120
+ * order the promises were provided). An exception is thrown if any of the
121
+ * promises are rejected.
122
+ *
123
+ * @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
124
+ *
125
+ * @return array
126
+ *
127
+ * @throws \Exception on error
128
+ * @throws \Throwable on error in PHP >=7
129
+ */
130
+ public static function unwrap($promises)
131
+ {
132
+ $results = [];
133
+ foreach ($promises as $key => $promise) {
134
+ $results[$key] = $promise->wait();
135
+ }
136
+
137
+ return $results;
138
+ }
139
+
140
+ /**
141
+ * Given an array of promises, return a promise that is fulfilled when all
142
+ * the items in the array are fulfilled.
143
+ *
144
+ * The promise's fulfillment value is an array with fulfillment values at
145
+ * respective positions to the original array. If any promise in the array
146
+ * rejects, the returned promise is rejected with the rejection reason.
147
+ *
148
+ * @param mixed $promises Promises or values.
149
+ * @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
150
+ *
151
+ * @return PromiseInterface
152
+ */
153
+ public static function all($promises, $recursive = false)
154
+ {
155
+ $results = [];
156
+ $promise = Each::of(
157
+ $promises,
158
+ function ($value, $idx) use (&$results) {
159
+ $results[$idx] = $value;
160
+ },
161
+ function ($reason, $idx, Promise $aggregate) {
162
+ $aggregate->reject($reason);
163
+ }
164
+ )->then(function () use (&$results) {
165
+ ksort($results);
166
+ return $results;
167
+ });
168
+
169
+ if (true === $recursive) {
170
+ $promise = $promise->then(function ($results) use ($recursive, &$promises) {
171
+ foreach ($promises as $promise) {
172
+ if (Is::pending($promise)) {
173
+ return self::all($promises, $recursive);
174
+ }
175
+ }
176
+ return $results;
177
+ });
178
+ }
179
+
180
+ return $promise;
181
+ }
182
+
183
+ /**
184
+ * Initiate a competitive race between multiple promises or values (values
185
+ * will become immediately fulfilled promises).
186
+ *
187
+ * When count amount of promises have been fulfilled, the returned promise
188
+ * is fulfilled with an array that contains the fulfillment values of the
189
+ * winners in order of resolution.
190
+ *
191
+ * This promise is rejected with a {@see AggregateException} if the number
192
+ * of fulfilled promises is less than the desired $count.
193
+ *
194
+ * @param int $count Total number of promises.
195
+ * @param mixed $promises Promises or values.
196
+ *
197
+ * @return PromiseInterface
198
+ */
199
+ public static function some($count, $promises)
200
+ {
201
+ $results = [];
202
+ $rejections = [];
203
+
204
+ return Each::of(
205
+ $promises,
206
+ function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
207
+ if (Is::settled($p)) {
208
+ return;
209
+ }
210
+ $results[$idx] = $value;
211
+ if (count($results) >= $count) {
212
+ $p->resolve(null);
213
+ }
214
+ },
215
+ function ($reason) use (&$rejections) {
216
+ $rejections[] = $reason;
217
+ }
218
+ )->then(
219
+ function () use (&$results, &$rejections, $count) {
220
+ if (count($results) !== $count) {
221
+ throw new AggregateException(
222
+ 'Not enough promises to fulfill count',
223
+ $rejections
224
+ );
225
+ }
226
+ ksort($results);
227
+ return array_values($results);
228
+ }
229
+ );
230
+ }
231
+
232
+ /**
233
+ * Like some(), with 1 as count. However, if the promise fulfills, the
234
+ * fulfillment value is not an array of 1 but the value directly.
235
+ *
236
+ * @param mixed $promises Promises or values.
237
+ *
238
+ * @return PromiseInterface
239
+ */
240
+ public static function any($promises)
241
+ {
242
+ return self::some(1, $promises)->then(function ($values) {
243
+ return $values[0];
244
+ });
245
+ }
246
+
247
+ /**
248
+ * Returns a promise that is fulfilled when all of the provided promises have
249
+ * been fulfilled or rejected.
250
+ *
251
+ * The returned promise is fulfilled with an array of inspection state arrays.
252
+ *
253
+ * @see inspect for the inspection state array format.
254
+ *
255
+ * @param mixed $promises Promises or values.
256
+ *
257
+ * @return PromiseInterface
258
+ */
259
+ public static function settle($promises)
260
+ {
261
+ $results = [];
262
+
263
+ return Each::of(
264
+ $promises,
265
+ function ($value, $idx) use (&$results) {
266
+ $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
267
+ },
268
+ function ($reason, $idx) use (&$results) {
269
+ $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
270
+ }
271
+ )->then(function () use (&$results) {
272
+ ksort($results);
273
+ return $results;
274
+ });
275
+ }
276
+ }
src/vendor/guzzlehttp/promises/src/functions.php ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace GuzzleHttp\Promise;
4
+
5
+ /**
6
+ * Get the global task queue used for promise resolution.
7
+ *
8
+ * This task queue MUST be run in an event loop in order for promises to be
9
+ * settled asynchronously. It will be automatically run when synchronously
10
+ * waiting on a promise.
11
+ *
12
+ * <code>
13
+ * while ($eventLoop->isRunning()) {
14
+ * GuzzleHttp\Promise\queue()->run();
15
+ * }
16
+ * </code>
17
+ *
18
+ * @param TaskQueueInterface $assign Optionally specify a new queue instance.
19
+ *
20
+ * @return TaskQueueInterface
21
+ *
22
+ * @deprecated queue will be removed in guzzlehttp/promises:2.0. Use Utils::queue instead.
23
+ */
24
+ function queue(TaskQueueInterface $assign = null)
25
+ {
26
+ return Utils::queue($assign);
27
+ }
28
+
29
+ /**
30
+ * Adds a function to run in the task queue when it is next `run()` and returns
31
+ * a promise that is fulfilled or rejected with the result.
32
+ *
33
+ * @param callable $task Task function to run.
34
+ *
35
+ * @return PromiseInterface
36
+ *
37
+ * @deprecated task will be removed in guzzlehttp/promises:2.0. Use Utils::task instead.
38
+ */
39
+ function task(callable $task)
40
+ {
41
+ return Utils::task($task);
42
+ }
43
+
44
+ /**
45
+ * Creates a promise for a value if the value is not a promise.
46
+ *
47
+ * @param mixed $value Promise or value.
48
+ *
49
+ * @return PromiseInterface
50
+ *
51
+ * @deprecated promise_for will be removed in guzzlehttp/promises:2.0. Use Create::promiseFor instead.
52
+ */
53
+ function promise_for($value)
54
+ {
55
+ return Create::promiseFor($value);
56
+ }
57
+
58
+ /**
59
+ * Creates a rejected promise for a reason if the reason is not a promise. If
60
+ * the provided reason is a promise, then it is returned as-is.
61
+ *
62
+ * @param mixed $reason Promise or reason.
63
+ *
64
+ * @return PromiseInterface
65
+ *
66
+ * @deprecated rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead.
67
+ */
68
+ function rejection_for($reason)
69
+ {
70
+ return Create::rejectionFor($reason);
71
+ }
72
+
73
+ /**
74
+ * Create an exception for a rejected promise value.
75
+ *
76
+ * @param mixed $reason
77
+ *
78
+ * @return \Exception|\Throwable
79
+ *
80
+ * @deprecated exception_for will be removed in guzzlehttp/promises:2.0. Use Create::exceptionFor instead.
81
+ */
82
+ function exception_for($reason)
83
+ {
84
+ return Create::exceptionFor($reason);
85
+ }
86
+
87
+ /**
88
+ * Returns an iterator for the given value.
89
+ *
90
+ * @param mixed $value
91
+ *
92
+ * @return \Iterator
93
+ *
94
+ * @deprecated iter_for will be removed in guzzlehttp/promises:2.0. Use Create::iterFor instead.
95
+ */
96
+ function iter_for($value)
97
+ {
98
+ return Create::iterFor($value);
99
+ }
100
+
101
+ /**
102
+ * Synchronously waits on a promise to resolve and returns an inspection state
103
+ * array.
104
+ *
105
+ * Returns a state associative array containing a "state" key mapping to a
106
+ * valid promise state. If the state of the promise is "fulfilled", the array
107
+ * will contain a "value" key mapping to the fulfilled value of the promise. If
108
+ * the promise is rejected, the array will contain a "reason" key mapping to
109
+ * the rejection reason of the promise.
110
+ *
111
+ * @param PromiseInterface $promise Promise or value.
112
+ *
113
+ * @return array
114
+ *
115
+ * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspect instead.
116
+ */
117
+ function inspect(PromiseInterface $promise)
118
+ {
119
+ return Utils::inspect($promise);
120
+ }
121
+
122
+ /**
123
+ * Waits on all of the provided promises, but does not unwrap rejected promises
124
+ * as thrown exception.
125
+ *
126
+ * Returns an array of inspection state arrays.
127
+ *
128
+ * @see inspect for the inspection state array format.
129
+ *
130
+ * @param PromiseInterface[] $promises Traversable of promises to wait upon.
131
+ *
132
+ * @return array
133
+ *
134
+ * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspectAll instead.
135
+ */
136
+ function inspect_all($promises)
137
+ {
138
+ return Utils::inspectAll($promises);
139
+ }
140
+
141
+ /**
142
+ * Waits on all of the provided promises and returns the fulfilled values.
143
+ *
144
+ * Returns an array that contains the value of each promise (in the same order
145
+ * the promises were provided). An exception is thrown if any of the promises
146
+ * are rejected.
147
+ *
148
+ * @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
149
+ *
150
+ * @return array
151
+ *
152
+ * @throws \Exception on error
153
+ * @throws \Throwable on error in PHP >=7
154
+ *
155
+ * @deprecated unwrap will be removed in guzzlehttp/promises:2.0. Use Utils::unwrap instead.
156
+ */
157
+ function unwrap($promises)
158
+ {
159
+ return Utils::unwrap($promises);
160
+ }
161
+
162
+ /**
163
+ * Given an array of promises, return a promise that is fulfilled when all the
164
+ * items in the array are fulfilled.
165
+ *
166
+ * The promise's fulfillment value is an array with fulfillment values at
167
+ * respective positions to the original array. If any promise in the array
168
+ * rejects, the returned promise is rejected with the rejection reason.
169
+ *
170
+ * @param mixed $promises Promises or values.
171
+ * @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
172
+ *
173
+ * @return PromiseInterface
174
+ *
175
+ * @deprecated all will be removed in guzzlehttp/promises:2.0. Use Utils::all instead.
176
+ */
177
+ function all($promises, $recursive = false)
178
+ {
179
+ return Utils::all($promises, $recursive);
180
+ }
181
+
182
+ /**
183
+ * Initiate a competitive race between multiple promises or values (values will
184
+ * become immediately fulfilled promises).
185
+ *
186
+ * When count amount of promises have been fulfilled, the returned promise is
187
+ * fulfilled with an array that contains the fulfillment values of the winners
188
+ * in order of resolution.
189
+ *
190
+ * This promise is rejected with a {@see AggregateException} if the number of
191
+ * fulfilled promises is less than the desired $count.
192
+ *
193
+ * @param int $count Total number of promises.
194
+ * @param mixed $promises Promises or values.
195
+ *
196
+ * @return PromiseInterface
197
+ *
198
+ * @deprecated some will be removed in guzzlehttp/promises:2.0. Use Utils::some instead.
199
+ */
200
+ function some($count, $promises)
201
+ {
202
+ return Utils::some($count, $promises);
203
+ }
204
+
205
+ /**
206
+ * Like some(), with 1 as count. However, if the promise fulfills, the
207
+ * fulfillment value is not an array of 1 but the value directly.
208
+ *
209
+ * @param mixed $promises Promises or values.
210
+ *
211
+ * @return PromiseInterface
212
+ *
213
+ * @deprecated any will be removed in guzzlehttp/promises:2.0. Use Utils::any instead.
214
+ */
215
+ function any($promises)
216
+ {
217
+ return Utils::any($promises);
218
+ }
219
+
220
+ /**
221
+ * Returns a promise that is fulfilled when all of the provided promises have
222
+ * been fulfilled or rejected.
223
+ *
224
+ * The returned promise is fulfilled with an array of inspection state arrays.
225
+ *
226
+ * @see inspect for the inspection state array format.
227
+ *
228
+ * @param mixed $promises Promises or values.
229
+ *
230
+ * @return PromiseInterface
231
+ *
232
+ * @deprecated settle will be removed in guzzlehttp/promises:2.0. Use Utils::settle instead.
233
+ */
234
+ function settle($promises)
235
+ {
236
+ return Utils::settle($promises);
237
+ }
238
+
239
+ /**
240
+ * Given an iterator that yields promises or values, returns a promise that is
241
+ * fulfilled with a null value when the iterator has been consumed or the
242
+ * aggregate promise has been fulfilled or rejected.
243
+ *
244
+ * $onFulfilled is a function that accepts the fulfilled value, iterator index,
245
+ * and the aggregate promise. The callback can invoke any necessary side
246
+ * effects and choose to resolve or reject the aggregate if needed.
247
+ *
248
+ * $onRejected is a function that accepts the rejection reason, iterator index,
249
+ * and the aggregate promise. The callback can invoke any necessary side
250
+ * effects and choose to resolve or reject the aggregate if needed.
251
+ *
252
+ * @param mixed $iterable Iterator or array to iterate over.
253
+ * @param callable $onFulfilled
254
+ * @param callable $onRejected
255
+ *
256
+ * @return PromiseInterface
257
+ *
258
+ * @deprecated each will be removed in guzzlehttp/promises:2.0. Use Each::of instead.
259
+ */
260
+ function each(
261
+ $iterable,
262
+ callable $onFulfilled = null,
263
+ callable $onRejected = null
264
+ ) {
265
+ return Each::of($iterable, $onFulfilled, $onRejected);
266
+ }
267
+
268
+ /**
269
+ * Like each, but only allows a certain number of outstanding promises at any
270
+ * given time.
271
+ *
272
+ * $concurrency may be an integer or a function that accepts the number of
273
+ * pending promises and returns a numeric concurrency limit value to allow for
274
+ * dynamic a concurrency size.
275
+ *
276
+ * @param mixed $iterable
277
+ * @param int|callable $concurrency
278
+ * @param callable $onFulfilled
279
+ * @param callable $onRejected
280
+ *
281
+ * @return PromiseInterface
282
+ *
283
+ * @deprecated each_limit will be removed in guzzlehttp/promises:2.0. Use Each::ofLimit instead.
284
+ */
285
+ function each_limit(
286
+ $iterable,
287
+ $concurrency,
288
+ callable $onFulfilled = null,
289
+ callable $onRejected = null
290
+ ) {
291
+ return Each::ofLimit($iterable, $concurrency, $onFulfilled, $onRejected);
292
+ }
293
+
294
+ /**
295
+ * Like each_limit, but ensures that no promise in the given $iterable argument
296
+ * is rejected. If any promise is rejected, then the aggregate promise is
297
+ * rejected with the encountered rejection.
298
+ *
299
+ * @param mixed $iterable
300
+ * @param int|callable $concurrency
301
+ * @param callable $onFulfilled
302
+ *
303
+ * @return PromiseInterface
304
+ *
305
+ * @deprecated each_limit_all will be removed in guzzlehttp/promises:2.0. Use Each::ofLimitAll instead.
306
+ */
307
+ function each_limit_all(
308
+ $iterable,
309
+ $concurrency,
310
+ callable $onFulfilled = null
311
+ ) {
312
+ return Each::ofLimitAll($iterable, $concurrency, $onFulfilled);
313
+ }
314
+
315
+ /**
316
+ * Returns true if a promise is fulfilled.
317
+ *
318
+ * @return bool
319
+ *
320
+ * @deprecated is_fulfilled will be removed in guzzlehttp/promises:2.0. Use Is::fulfilled instead.
321
+ */
322
+ function is_fulfilled(PromiseInterface $promise)
323
+ {
324
+ return Is::fulfilled($promise);
325
+ }
326
+
327
+ /**
328
+ * Returns true if a promise is rejected.
329
+ *
330
+ * @return bool
331
+ *
332
+ * @deprecated is_rejected will be removed in guzzlehttp/promises:2.0. Use Is::rejected instead.
333
+ */
334
+ function is_rejected(PromiseInterface $promise)
335
+ {
336
+ return Is::rejected($promise);
337
+ }
338
+
339
+ /**
340
+ * Returns true if a promise is fulfilled or rejected.
341
+ *
342
+ * @return bool
343
+ *
344
+ * @deprecated is_settled will be removed in guzzlehttp/promises:2.0. Use Is::settled instead.
345
+ */
346
+ function is_settled(PromiseInterface $promise)
347
+ {
348
+ return Is::settled($promise);
349
+ }
350
+
351
+ /**
352
+ * Create a new coroutine.
353
+ *
354
+ * @see Coroutine
355
+ *
356
+ * @return PromiseInterface
357
+ *
358
+ * @deprecated coroutine will be removed in guzzlehttp/promises:2.0. Use Coroutine::of instead.
359
+ */
360
+ function coroutine(callable $generatorFn)
361
+ {
362
+ return Coroutine::of($generatorFn);
363
+ }
src/vendor/guzzlehttp/promises/src/functions_include.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Don't redefine the functions if included multiple times.
4
+ if (!function_exists('GuzzleHttp\Promise\promise_for')) {
5
+ require __DIR__ . '/functions.php';
6
+ }
src/vendor/guzzlehttp/psr7/CHANGELOG.md ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## Unreleased
9
+
10
+ ## 2.3.0 - 2022-06-09
11
+
12
+ ### Fixed
13
+
14
+ - Added `Header::splitList()`
15
+ - Added `Utils::tryGetContents`
16
+ - Improved `Stream::getContents()`
17
+ - Updated mimetype mappings
18
+
19
+ ## 2.2.2 - 2022-06-08
20
+
21
+ ### Fixed
22
+
23
+ - Fix `Message::parseRequestUri()` for numeric headers
24
+ - Re-wrap exceptions thrown in `fread` into runtime exceptions
25
+ - Throw an exception when multipart options is misformatted
26
+
27
+ ## 2.2.1 - 2022-03-20
28
+
29
+ ### Fixed
30
+
31
+ - Correct header value validation
32
+
33
+ ## 2.2.0 - 2022-03-20
34
+
35
+ ### Added
36
+
37
+ - A more compressive list of mime types
38
+ - Add JsonSerializable to Uri
39
+ - Missing return types
40
+
41
+ ### Fixed
42
+
43
+ - Bug MultipartStream no `uri` metadata
44
+ - Bug MultipartStream with filename for `data://` streams
45
+ - Fixed new line handling in MultipartStream
46
+ - Reduced RAM usage when copying streams
47
+ - Updated parsing in `Header::normalize()`
48
+
49
+ ## 2.1.1 - 2022-03-20
50
+
51
+ ### Fixed
52
+
53
+ - Validate header values properly
54
+
55
+ ## 2.1.0 - 2021-10-06
56
+
57
+ ### Changed
58
+
59
+ - Attempting to create a `Uri` object from a malformed URI will no longer throw a generic
60
+ `InvalidArgumentException`, but rather a `MalformedUriException`, which inherits from the former
61
+ for backwards compatibility. Callers relying on the exception being thrown to detect invalid
62
+ URIs should catch the new exception.
63
+
64
+ ### Fixed
65
+
66
+ - Return `null` in caching stream size if remote size is `null`
67
+
68
+ ## 2.0.0 - 2021-06-30
69
+
70
+ Identical to the RC release.
71
+
72
+ ## 2.0.0@RC-1 - 2021-04-29
73
+
74
+ ### Fixed
75
+
76
+ - Handle possibly unset `url` in `stream_get_meta_data`
77
+
78
+ ## 2.0.0@beta-1 - 2021-03-21
79
+
80
+ ### Added
81
+
82
+ - PSR-17 factories
83
+ - Made classes final
84
+ - PHP7 type hints
85
+
86
+ ### Changed
87
+
88
+ - When building a query string, booleans are represented as 1 and 0.
89
+
90
+ ### Removed
91
+
92
+ - PHP < 7.2 support
93
+ - All functions in the Guzzle\Psr7 namespace
94
+
95
+ ## 1.8.1 - 2021-03-21
96
+
97
+ ### Fixed
98
+
99
+ - Issue parsing IPv6 URLs
100
+ - Issue modifying ServerRequest lost all its attributes
101
+
102
+ ## 1.8.0 - 2021-03-21
103
+
104
+ ### Added
105
+
106
+ - Locale independent URL parsing
107
+ - Most classes got a `@final` annotation to prepare for 2.0
108
+
109
+ ### Fixed
110
+
111
+ - Issue when creating stream from `php://input` and curl-ext is not installed
112
+ - Broken `Utils::tryFopen()` on PHP 8
113
+
114
+ ## 1.7.0 - 2020-09-30
115
+
116
+ ### Added
117
+
118
+ - Replaced functions by static methods
119
+
120
+ ### Fixed
121
+
122
+ - Converting a non-seekable stream to a string
123
+ - Handle multiple Set-Cookie correctly
124
+ - Ignore array keys in header values when merging
125
+ - Allow multibyte characters to be parsed in `Message:bodySummary()`
126
+
127
+ ### Changed
128
+
129
+ - Restored partial HHVM 3 support
130
+
131
+
132
+ ## [1.6.1] - 2019-07-02
133
+
134
+ ### Fixed
135
+
136
+ - Accept null and bool header values again
137
+
138
+
139
+ ## [1.6.0] - 2019-06-30
140
+
141
+ ### Added
142
+
143
+ - Allowed version `^3.0` of `ralouphie/getallheaders` dependency (#244)
144
+ - Added MIME type for WEBP image format (#246)
145
+ - Added more validation of values according to PSR-7 and RFC standards, e.g. status code range (#250, #272)
146
+
147
+ ### Changed
148
+
149
+ - Tests don't pass with HHVM 4.0, so HHVM support got dropped. Other libraries like composer have done the same. (#262)
150
+ - Accept port number 0 to be valid (#270)
151
+
152
+ ### Fixed
153
+
154
+ - Fixed subsequent reads from `php://input` in ServerRequest (#247)
155
+ - Fixed readable/writable detection for certain stream modes (#248)
156
+ - Fixed encoding of special characters in the `userInfo` component of an URI (#253)
157
+
158
+
159
+ ## [1.5.2] - 2018-12-04
160
+
161
+ ### Fixed
162
+
163
+ - Check body size when getting the message summary
164
+
165
+
166
+ ## [1.5.1] - 2018-12-04
167
+
168
+ ### Fixed
169
+
170
+ - Get the summary of a body only if it is readable
171
+
172
+
173
+ ## [1.5.0] - 2018-12-03
174
+
175
+ ### Added
176
+
177
+ - Response first-line to response string exception (fixes #145)
178
+ - A test for #129 behavior
179
+ - `get_message_body_summary` function in order to get the message summary
180
+ - `3gp` and `mkv` mime types
181
+
182
+ ### Changed
183
+
184
+ - Clarify exception message when stream is detached
185
+
186
+ ### Deprecated
187
+
188
+ - Deprecated parsing folded header lines as per RFC 7230
189
+
190
+ ### Fixed
191
+
192
+ - Fix `AppendStream::detach` to not close streams
193
+ - `InflateStream` preserves `isSeekable` attribute of the underlying stream
194
+ - `ServerRequest::getUriFromGlobals` to support URLs in query parameters
195
+
196
+
197
+ Several other fixes and improvements.
198
+
199
+
200
+ ## [1.4.2] - 2017-03-20
201
+
202
+ ### Fixed
203
+
204
+ - Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing
205
+ calls to `trigger_error` when deprecated methods are invoked.
206
+
207
+
208
+ ## [1.4.1] - 2017-02-27
209
+
210
+ ### Added
211
+
212
+ - Rriggering of silenced deprecation warnings.
213
+
214
+ ### Fixed
215
+
216
+ - Reverted BC break by reintroducing behavior to automagically fix a URI with a
217
+ relative path and an authority by adding a leading slash to the path. It's only
218
+ deprecated now.
219
+
220
+
221
+ ## [1.4.0] - 2017-02-21
222
+
223
+ ### Added
224
+
225
+ - Added common URI utility methods based on RFC 3986 (see documentation in the readme):
226
+ - `Uri::isDefaultPort`
227
+ - `Uri::isAbsolute`
228
+ - `Uri::isNetworkPathReference`
229
+ - `Uri::isAbsolutePathReference`
230
+ - `Uri::isRelativePathReference`
231
+ - `Uri::isSameDocumentReference`
232
+ - `Uri::composeComponents`
233
+ - `UriNormalizer::normalize`
234
+ - `UriNormalizer::isEquivalent`
235
+ - `UriResolver::relativize`
236
+
237
+ ### Changed
238
+
239
+ - Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form.
240
+ - Allow `parse_response` to parse a response without delimiting space and reason.
241
+ - Ensure each URI modification results in a valid URI according to PSR-7 discussions.
242
+ Invalid modifications will throw an exception instead of returning a wrong URI or
243
+ doing some magic.
244
+ - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception
245
+ because the path of a URI with an authority must start with a slash "/" or be empty
246
+ - `(new Uri())->withScheme('http')` will return `'http://localhost'`
247
+
248
+ ### Deprecated
249
+
250
+ - `Uri::resolve` in favor of `UriResolver::resolve`
251
+ - `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`
252
+
253
+ ### Fixed
254
+
255
+ - `Stream::read` when length parameter <= 0.
256
+ - `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
257
+ - `ServerRequest::getUriFromGlobals` when `Host` header contains port.
258
+ - Compatibility of URIs with `file` scheme and empty host.
259
+
260
+
261
+ ## [1.3.1] - 2016-06-25
262
+
263
+ ### Fixed
264
+
265
+ - `Uri::__toString` for network path references, e.g. `//example.org`.
266
+ - Missing lowercase normalization for host.
267
+ - Handling of URI components in case they are `'0'` in a lot of places,
268
+ e.g. as a user info password.
269
+ - `Uri::withAddedHeader` to correctly merge headers with different case.
270
+ - Trimming of header values in `Uri::withAddedHeader`. Header values may
271
+ be surrounded by whitespace which should be ignored according to RFC 7230
272
+ Section 3.2.4. This does not apply to header names.
273
+ - `Uri::withAddedHeader` with an array of header values.
274
+ - `Uri::resolve` when base path has no slash and handling of fragment.
275
+ - Handling of encoding in `Uri::with(out)QueryValue` so one can pass the
276
+ key/value both in encoded as well as decoded form to those methods. This is
277
+ consistent with withPath, withQuery etc.
278
+ - `ServerRequest::withoutAttribute` when attribute value is null.
279
+
280
+
281
+ ## [1.3.0] - 2016-04-13
282
+
283
+ ### Added
284
+
285
+ - Remaining interfaces needed for full PSR7 compatibility
286
+ (ServerRequestInterface, UploadedFileInterface, etc.).
287
+ - Support for stream_for from scalars.
288
+
289
+ ### Changed
290
+
291
+ - Can now extend Uri.
292
+
293
+ ### Fixed
294
+ - A bug in validating request methods by making it more permissive.
295
+
296
+
297
+ ## [1.2.3] - 2016-02-18
298
+
299
+ ### Fixed
300
+
301
+ - Support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
302
+ streams, which can sometimes return fewer bytes than requested with `fread`.
303
+ - Handling of gzipped responses with FNAME headers.
304
+
305
+
306
+ ## [1.2.2] - 2016-01-22
307
+
308
+ ### Added
309
+
310
+ - Support for URIs without any authority.
311
+ - Support for HTTP 451 'Unavailable For Legal Reasons.'
312
+ - Support for using '0' as a filename.
313
+ - Support for including non-standard ports in Host headers.
314
+
315
+
316
+ ## [1.2.1] - 2015-11-02
317
+
318
+ ### Changes
319
+
320
+ - Now supporting negative offsets when seeking to SEEK_END.
321
+
322
+
323
+ ## [1.2.0] - 2015-08-15
324
+
325
+ ### Changed
326
+
327
+ - Body as `"0"` is now properly added to a response.
328
+ - Now allowing forward seeking in CachingStream.
329
+ - Now properly parsing HTTP requests that contain proxy targets in
330
+ `parse_request`.
331
+ - functions.php is now conditionally required.
332
+ - user-info is no longer dropped when resolving URIs.
333
+
334
+
335
+ ## [1.1.0] - 2015-06-24
336
+
337
+ ### Changed
338
+
339
+ - URIs can now be relative.
340
+ - `multipart/form-data` headers are now overridden case-insensitively.
341
+ - URI paths no longer encode the following characters because they are allowed
342
+ in URIs: "(", ")", "*", "!", "'"
343
+ - A port is no longer added to a URI when the scheme is missing and no port is
344
+ present.
345
+
346
+
347
+ ## 1.0.0 - 2015-05-19
348
+
349
+ Initial release.
350
+
351
+ Currently unsupported:
352
+
353
+ - `Psr\Http\Message\ServerRequestInterface`
354
+ - `Psr\Http\Message\UploadedFileInterface`
355
+
356
+
357
+
358
+ [1.6.0]: https://github.com/guzzle/psr7/compare/1.5.2...1.6.0
359
+ [1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2
360
+ [1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1
361
+ [1.5.0]: https://github.com/guzzle/psr7/compare/1.4.2...1.5.0
362
+ [1.4.2]: https://github.com/guzzle/psr7/compare/1.4.1...1.4.2
363
+ [1.4.1]: https://github.com/guzzle/psr7/compare/1.4.0...1.4.1
364
+ [1.4.0]: https://github.com/guzzle/psr7/compare/1.3.1...1.4.0
365
+ [1.3.1]: https://github.com/guzzle/psr7/compare/1.3.0...1.3.1
366
+ [1.3.0]: https://github.com/guzzle/psr7/compare/1.2.3...1.3.0
367
+ [1.2.3]: https://github.com/guzzle/psr7/compare/1.2.2...1.2.3
368
+ [1.2.2]: https://github.com/guzzle/psr7/compare/1.2.1...1.2.2
369
+ [1.2.1]: https://github.com/guzzle/psr7/compare/1.2.0...1.2.1
370
+ [1.2.0]: https://github.com/guzzle/psr7/compare/1.1.0...1.2.0
371
+ [1.1.0]: https://github.com/guzzle/psr7/compare/1.0.0...1.1.0
src/vendor/guzzlehttp/psr7/LICENSE ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Michael Dowling <mtdowling@gmail.com>
4
+ Copyright (c) 2015 Márk Sági-Kazár <mark.sagikazar@gmail.com>
5
+ Copyright (c) 2015 Graham Campbell <hello@gjcampbell.co.uk>
6
+ Copyright (c) 2016 Tobias Schultze <webmaster@tubo-world.de>
7
+ Copyright (c) 2016 George Mponos <gmponos@gmail.com>
8
+ Copyright (c) 2018 Tobias Nyholm <tobias.nyholm@gmail.com>
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in
18
+ all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ THE SOFTWARE.
src/vendor/guzzlehttp/psr7/README.md ADDED
@@ -0,0 +1,840 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PSR-7 Message Implementation
2
+
3
+ This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/)
4
+ message implementation, several stream decorators, and some helpful
5
+ functionality like query string parsing.
6
+
7
+ ![CI](https://github.com/guzzle/psr7/workflows/CI/badge.svg)
8
+ ![Static analysis](https://github.com/guzzle/psr7/workflows/Static%20analysis/badge.svg)
9
+
10
+
11
+ # Stream implementation
12
+
13
+ This package comes with a number of stream implementations and stream
14
+ decorators.
15
+
16
+
17
+ ## AppendStream
18
+
19
+ `GuzzleHttp\Psr7\AppendStream`
20
+
21
+ Reads from multiple streams, one after the other.
22
+
23
+ ```php
24
+ use GuzzleHttp\Psr7;
25
+
26
+ $a = Psr7\Utils::streamFor('abc, ');
27
+ $b = Psr7\Utils::streamFor('123.');
28
+ $composed = new Psr7\AppendStream([$a, $b]);
29
+
30
+ $composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));
31
+
32
+ echo $composed; // abc, 123. Above all listen to me.
33
+ ```
34
+
35
+
36
+ ## BufferStream
37
+
38
+ `GuzzleHttp\Psr7\BufferStream`
39
+
40
+ Provides a buffer stream that can be written to fill a buffer, and read
41
+ from to remove bytes from the buffer.
42
+
43
+ This stream returns a "hwm" metadata value that tells upstream consumers
44
+ what the configured high water mark of the stream is, or the maximum
45
+ preferred size of the buffer.
46
+
47
+ ```php
48
+ use GuzzleHttp\Psr7;
49
+
50
+ // When more than 1024 bytes are in the buffer, it will begin returning
51
+ // false to writes. This is an indication that writers should slow down.
52
+ $buffer = new Psr7\BufferStream(1024);
53
+ ```
54
+
55
+
56
+ ## CachingStream
57
+
58
+ The CachingStream is used to allow seeking over previously read bytes on
59
+ non-seekable streams. This can be useful when transferring a non-seekable
60
+ entity body fails due to needing to rewind the stream (for example, resulting
61
+ from a redirect). Data that is read from the remote stream will be buffered in
62
+ a PHP temp stream so that previously read bytes are cached first in memory,
63
+ then on disk.
64
+
65
+ ```php
66
+ use GuzzleHttp\Psr7;
67
+
68
+ $original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
69
+ $stream = new Psr7\CachingStream($original);
70
+
71
+ $stream->read(1024);
72
+ echo $stream->tell();
73
+ // 1024
74
+
75
+ $stream->seek(0);
76
+ echo $stream->tell();
77
+ // 0
78
+ ```
79
+
80
+
81
+ ## DroppingStream
82
+
83
+ `GuzzleHttp\Psr7\DroppingStream`
84
+
85
+ Stream decorator that begins dropping data once the size of the underlying
86
+ stream becomes too full.
87
+
88
+ ```php
89
+ use GuzzleHttp\Psr7;
90
+
91
+ // Create an empty stream
92
+ $stream = Psr7\Utils::streamFor();
93
+
94
+ // Start dropping data when the stream has more than 10 bytes
95
+ $dropping = new Psr7\DroppingStream($stream, 10);
96
+
97
+ $dropping->write('01234567890123456789');
98
+ echo $stream; // 0123456789
99
+ ```
100
+
101
+
102
+ ## FnStream
103
+
104
+ `GuzzleHttp\Psr7\FnStream`
105
+
106
+ Compose stream implementations based on a hash of functions.
107
+
108
+ Allows for easy testing and extension of a provided stream without needing
109
+ to create a concrete class for a simple extension point.
110
+
111
+ ```php
112
+
113
+ use GuzzleHttp\Psr7;
114
+
115
+ $stream = Psr7\Utils::streamFor('hi');
116
+ $fnStream = Psr7\FnStream::decorate($stream, [
117
+ 'rewind' => function () use ($stream) {
118
+ echo 'About to rewind - ';
119
+ $stream->rewind();
120
+ echo 'rewound!';
121
+ }
122
+ ]);
123
+
124
+ $fnStream->rewind();
125
+ // Outputs: About to rewind - rewound!
126
+ ```
127
+
128
+
129
+ ## InflateStream
130
+
131
+ `GuzzleHttp\Psr7\InflateStream`
132
+
133
+ Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.
134
+
135
+ This stream decorator converts the provided stream to a PHP stream resource,
136
+ then appends the zlib.inflate filter. The stream is then converted back
137
+ to a Guzzle stream resource to be used as a Guzzle stream.
138
+
139
+
140
+ ## LazyOpenStream
141
+
142
+ `GuzzleHttp\Psr7\LazyOpenStream`
143
+
144
+ Lazily reads or writes to a file that is opened only after an IO operation
145
+ take place on the stream.
146
+
147
+ ```php
148
+ use GuzzleHttp\Psr7;
149
+
150
+ $stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
151
+ // The file has not yet been opened...
152
+
153
+ echo $stream->read(10);
154
+ // The file is opened and read from only when needed.
155
+ ```
156
+
157
+
158
+ ## LimitStream
159
+
160
+ `GuzzleHttp\Psr7\LimitStream`
161
+
162
+ LimitStream can be used to read a subset or slice of an existing stream object.
163
+ This can be useful for breaking a large file into smaller pieces to be sent in
164
+ chunks (e.g. Amazon S3's multipart upload API).
165
+
166
+ ```php
167
+ use GuzzleHttp\Psr7;
168
+
169
+ $original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
170
+ echo $original->getSize();
171
+ // >>> 1048576
172
+
173
+ // Limit the size of the body to 1024 bytes and start reading from byte 2048
174
+ $stream = new Psr7\LimitStream($original, 1024, 2048);
175
+ echo $stream->getSize();
176
+ // >>> 1024
177
+ echo $stream->tell();
178
+ // >>> 0
179
+ ```
180
+
181
+
182
+ ## MultipartStream
183
+
184
+ `GuzzleHttp\Psr7\MultipartStream`
185
+
186
+ Stream that when read returns bytes for a streaming multipart or
187
+ multipart/form-data stream.
188
+
189
+
190
+ ## NoSeekStream
191
+
192
+ `GuzzleHttp\Psr7\NoSeekStream`
193
+
194
+ NoSeekStream wraps a stream and does not allow seeking.
195
+
196
+ ```php
197
+ use GuzzleHttp\Psr7;
198
+
199
+ $original = Psr7\Utils::streamFor('foo');
200
+ $noSeek = new Psr7\NoSeekStream($original);
201
+
202
+ echo $noSeek->read(3);
203
+ // foo
204
+ var_export($noSeek->isSeekable());
205
+ // false
206
+ $noSeek->seek(0);
207
+ var_export($noSeek->read(3));
208
+ // NULL
209
+ ```
210
+
211
+
212
+ ## PumpStream
213
+
214
+ `GuzzleHttp\Psr7\PumpStream`
215
+
216
+ Provides a read only stream that pumps data from a PHP callable.
217
+
218
+ When invoking the provided callable, the PumpStream will pass the amount of
219
+ data requested to read to the callable. The callable can choose to ignore
220
+ this value and return fewer or more bytes than requested. Any extra data
221
+ returned by the provided callable is buffered internally until drained using
222
+ the read() function of the PumpStream. The provided callable MUST return
223
+ false when there is no more data to read.
224
+
225
+
226
+ ## Implementing stream decorators
227
+
228
+ Creating a stream decorator is very easy thanks to the
229
+ `GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
230
+ implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
231
+ stream. Just `use` the `StreamDecoratorTrait` and implement your custom
232
+ methods.
233
+
234
+ For example, let's say we wanted to call a specific function each time the last
235
+ byte is read from a stream. This could be implemented by overriding the
236
+ `read()` method.
237
+
238
+ ```php
239
+ use Psr\Http\Message\StreamInterface;
240
+ use GuzzleHttp\Psr7\StreamDecoratorTrait;
241
+
242
+ class EofCallbackStream implements StreamInterface
243
+ {
244
+ use StreamDecoratorTrait;
245
+
246
+ private $callback;
247
+
248
+ public function __construct(StreamInterface $stream, callable $cb)
249
+ {
250
+ $this->stream = $stream;
251
+ $this->callback = $cb;
252
+ }
253
+
254
+ public function read($length)
255
+ {
256
+ $result = $this->stream->read($length);
257
+
258
+ // Invoke the callback when EOF is hit.
259
+ if ($this->eof()) {
260
+ call_user_func($this->callback);
261
+ }
262
+
263
+ return $result;
264
+ }
265
+ }
266
+ ```
267
+
268
+ This decorator could be added to any existing stream and used like so:
269
+
270
+ ```php
271
+ use GuzzleHttp\Psr7;
272
+
273
+ $original = Psr7\Utils::streamFor('foo');
274
+
275
+ $eofStream = new EofCallbackStream($original, function () {
276
+ echo 'EOF!';
277
+ });
278
+
279
+ $eofStream->read(2);
280
+ $eofStream->read(1);
281
+ // echoes "EOF!"
282
+ $eofStream->seek(0);
283
+ $eofStream->read(3);
284
+ // echoes "EOF!"
285
+ ```
286
+
287
+
288
+ ## PHP StreamWrapper
289
+
290
+ You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
291
+ PSR-7 stream as a PHP stream resource.
292
+
293
+ Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
294
+ stream from a PSR-7 stream.
295
+
296
+ ```php
297
+ use GuzzleHttp\Psr7\StreamWrapper;
298
+
299
+ $stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
300
+ $resource = StreamWrapper::getResource($stream);
301
+ echo fread($resource, 6); // outputs hello!
302
+ ```
303
+
304
+
305
+ # Static API
306
+
307
+ There are various static methods available under the `GuzzleHttp\Psr7` namespace.
308
+
309
+
310
+ ## `GuzzleHttp\Psr7\Message::toString`
311
+
312
+ `public static function toString(MessageInterface $message): string`
313
+
314
+ Returns the string representation of an HTTP message.
315
+
316
+ ```php
317
+ $request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
318
+ echo GuzzleHttp\Psr7\Message::toString($request);
319
+ ```
320
+
321
+
322
+ ## `GuzzleHttp\Psr7\Message::bodySummary`
323
+
324
+ `public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null`
325
+
326
+ Get a short summary of the message body.
327
+
328
+ Will return `null` if the response is not printable.
329
+
330
+
331
+ ## `GuzzleHttp\Psr7\Message::rewindBody`
332
+
333
+ `public static function rewindBody(MessageInterface $message): void`
334
+
335
+ Attempts to rewind a message body and throws an exception on failure.
336
+
337
+ The body of the message will only be rewound if a call to `tell()`
338
+ returns a value other than `0`.
339
+
340
+
341
+ ## `GuzzleHttp\Psr7\Message::parseMessage`
342
+
343
+ `public static function parseMessage(string $message): array`
344
+
345
+ Parses an HTTP message into an associative array.
346
+
347
+ The array contains the "start-line" key containing the start line of
348
+ the message, "headers" key containing an associative array of header
349
+ array values, and a "body" key containing the body of the message.
350
+
351
+
352
+ ## `GuzzleHttp\Psr7\Message::parseRequestUri`
353
+
354
+ `public static function parseRequestUri(string $path, array $headers): string`
355
+
356
+ Constructs a URI for an HTTP request message.
357
+
358
+
359
+ ## `GuzzleHttp\Psr7\Message::parseRequest`
360
+
361
+ `public static function parseRequest(string $message): Request`
362
+
363
+ Parses a request message string into a request object.
364
+
365
+
366
+ ## `GuzzleHttp\Psr7\Message::parseResponse`
367
+
368
+ `public static function parseResponse(string $message): Response`
369
+
370
+ Parses a response message string into a response object.
371
+
372
+
373
+ ## `GuzzleHttp\Psr7\Header::parse`
374
+
375
+ `public static function parse(string|array $header): array`
376
+
377
+ Parse an array of header values containing ";" separated data into an
378
+ array of associative arrays representing the header key value pair data
379
+ of the header. When a parameter does not contain a value, but just
380
+ contains a key, this function will inject a key with a '' string value.
381
+
382
+
383
+ ## `GuzzleHttp\Psr7\Header::normalize`
384
+
385
+ `public static function normalize(string|array $header): array`
386
+
387
+ Converts an array of header values that may contain comma separated
388
+ headers into an array of headers with no comma separated values.
389
+
390
+
391
+ ## `GuzzleHttp\Psr7\Query::parse`
392
+
393
+ `public static function parse(string $str, int|bool $urlEncoding = true): array`
394
+
395
+ Parse a query string into an associative array.
396
+
397
+ If multiple values are found for the same key, the value of that key
398
+ value pair will become an array. This function does not parse nested
399
+ PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
400
+ will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
401
+
402
+
403
+ ## `GuzzleHttp\Psr7\Query::build`
404
+
405
+ `public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string`
406
+
407
+ Build a query string from an array of key value pairs.
408
+
409
+ This function can use the return value of `parse()` to build a query
410
+ string. This function does not modify the provided keys when an array is
411
+ encountered (like `http_build_query()` would).
412
+
413
+
414
+ ## `GuzzleHttp\Psr7\Utils::caselessRemove`
415
+
416
+ `public static function caselessRemove(iterable<string> $keys, $keys, array $data): array`
417
+
418
+ Remove the items given by the keys, case insensitively from the data.
419
+
420
+
421
+ ## `GuzzleHttp\Psr7\Utils::copyToStream`
422
+
423
+ `public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void`
424
+
425
+ Copy the contents of a stream into another stream until the given number
426
+ of bytes have been read.
427
+
428
+
429
+ ## `GuzzleHttp\Psr7\Utils::copyToString`
430
+
431
+ `public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`
432
+
433
+ Copy the contents of a stream into a string until the given number of
434
+ bytes have been read.
435
+
436
+
437
+ ## `GuzzleHttp\Psr7\Utils::hash`
438
+
439
+ `public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`
440
+
441
+ Calculate a hash of a stream.
442
+
443
+ This method reads the entire stream to calculate a rolling hash, based on
444
+ PHP's `hash_init` functions.
445
+
446
+
447
+ ## `GuzzleHttp\Psr7\Utils::modifyRequest`
448
+
449
+ `public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`
450
+
451
+ Clone and modify a request with the given changes.
452
+
453
+ This method is useful for reducing the number of clones needed to mutate
454
+ a message.
455
+
456
+ - method: (string) Changes the HTTP method.
457
+ - set_headers: (array) Sets the given headers.
458
+ - remove_headers: (array) Remove the given headers.
459
+ - body: (mixed) Sets the given body.
460
+ - uri: (UriInterface) Set the URI.
461
+ - query: (string) Set the query string value of the URI.
462
+ - version: (string) Set the protocol version.
463
+
464
+
465
+ ## `GuzzleHttp\Psr7\Utils::readLine`
466
+
467
+ `public static function readLine(StreamInterface $stream, int $maxLength = null): string`
468
+
469
+ Read a line from the stream up to the maximum allowed buffer length.
470
+
471
+
472
+ ## `GuzzleHttp\Psr7\Utils::streamFor`
473
+
474
+ `public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`
475
+
476
+ Create a new stream based on the input type.
477
+
478
+ Options is an associative array that can contain the following keys:
479
+
480
+ - metadata: Array of custom metadata.
481
+ - size: Size of the stream.
482
+
483
+ This method accepts the following `$resource` types:
484
+
485
+ - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
486
+ - `string`: Creates a stream object that uses the given string as the contents.
487
+ - `resource`: Creates a stream object that wraps the given PHP stream resource.
488
+ - `Iterator`: If the provided value implements `Iterator`, then a read-only
489
+ stream object will be created that wraps the given iterable. Each time the
490
+ stream is read from, data from the iterator will fill a buffer and will be
491
+ continuously called until the buffer is equal to the requested read size.
492
+ Subsequent read calls will first read from the buffer and then call `next`
493
+ on the underlying iterator until it is exhausted.
494
+ - `object` with `__toString()`: If the object has the `__toString()` method,
495
+ the object will be cast to a string and then a stream will be returned that
496
+ uses the string value.
497
+ - `NULL`: When `null` is passed, an empty stream object is returned.
498
+ - `callable` When a callable is passed, a read-only stream object will be
499
+ created that invokes the given callable. The callable is invoked with the
500
+ number of suggested bytes to read. The callable can return any number of
501
+ bytes, but MUST return `false` when there is no more data to return. The
502
+ stream object that wraps the callable will invoke the callable until the
503
+ number of requested bytes are available. Any additional bytes will be
504
+ buffered and used in subsequent reads.
505
+
506
+ ```php
507
+ $stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
508
+ $stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));
509
+
510
+ $generator = function ($bytes) {
511
+ for ($i = 0; $i < $bytes; $i++) {
512
+ yield ' ';
513
+ }
514
+ }
515
+
516
+ $stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
517
+ ```
518
+
519
+
520
+ ## `GuzzleHttp\Psr7\Utils::tryFopen`
521
+
522
+ `public static function tryFopen(string $filename, string $mode): resource`
523
+
524
+ Safely opens a PHP stream resource using a filename.
525
+
526
+ When fopen fails, PHP normally raises a warning. This function adds an
527
+ error handler that checks for errors and throws an exception instead.
528
+
529
+
530
+ ## `GuzzleHttp\Psr7\Utils::tryGetContents`
531
+
532
+ `public static function tryGetContents(resource $stream): string`
533
+
534
+ Safely gets the contents of a given stream.
535
+
536
+ When stream_get_contents fails, PHP normally raises a warning. This
537
+ function adds an error handler that checks for errors and throws an
538
+ exception instead.
539
+
540
+
541
+ ## `GuzzleHttp\Psr7\Utils::uriFor`
542
+
543
+ `public static function uriFor(string|UriInterface $uri): UriInterface`
544
+
545
+ Returns a UriInterface for the given value.
546
+
547
+ This function accepts a string or UriInterface and returns a
548
+ UriInterface for the given value. If the value is already a
549
+ UriInterface, it is returned as-is.
550
+
551
+
552
+ ## `GuzzleHttp\Psr7\MimeType::fromFilename`
553
+
554
+ `public static function fromFilename(string $filename): string|null`
555
+
556
+ Determines the mimetype of a file by looking at its extension.
557
+
558
+
559
+ ## `GuzzleHttp\Psr7\MimeType::fromExtension`
560
+
561
+ `public static function fromExtension(string $extension): string|null`
562
+
563
+ Maps a file extensions to a mimetype.
564
+
565
+
566
+ ## Upgrading from Function API
567
+
568
+ The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API was removed in 2.0.0. A migration table has been provided here for your convenience:
569
+
570
+ | Original Function | Replacement Method |
571
+ |----------------|----------------|
572
+ | `str` | `Message::toString` |
573
+ | `uri_for` | `Utils::uriFor` |
574
+ | `stream_for` | `Utils::streamFor` |
575
+ | `parse_header` | `Header::parse` |
576
+ | `normalize_header` | `Header::normalize` |
577
+ | `modify_request` | `Utils::modifyRequest` |
578
+ | `rewind_body` | `Message::rewindBody` |
579
+ | `try_fopen` | `Utils::tryFopen` |
580
+ | `copy_to_string` | `Utils::copyToString` |
581
+ | `copy_to_stream` | `Utils::copyToStream` |
582
+ | `hash` | `Utils::hash` |
583
+ | `readline` | `Utils::readLine` |
584
+ | `parse_request` | `Message::parseRequest` |
585
+ | `parse_response` | `Message::parseResponse` |
586
+ | `parse_query` | `Query::parse` |
587
+ | `build_query` | `Query::build` |
588
+ | `mimetype_from_filename` | `MimeType::fromFilename` |
589
+ | `mimetype_from_extension` | `MimeType::fromExtension` |
590
+ | `_parse_message` | `Message::parseMessage` |
591
+ | `_parse_request_uri` | `Message::parseRequestUri` |
592
+ | `get_message_body_summary` | `Message::bodySummary` |
593
+ | `_caseless_remove` | `Utils::caselessRemove` |
594
+
595
+
596
+ # Additional URI Methods
597
+
598
+ Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
599
+ this library also provides additional functionality when working with URIs as static methods.
600
+
601
+ ## URI Types
602
+
603
+ An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
604
+ An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
605
+ the base URI. Relative references can be divided into several forms according to
606
+ [RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):
607
+
608
+ - network-path references, e.g. `//example.com/path`
609
+ - absolute-path references, e.g. `/path`
610
+ - relative-path references, e.g. `subpath`
611
+
612
+ The following methods can be used to identify the type of the URI.
613
+
614
+ ### `GuzzleHttp\Psr7\Uri::isAbsolute`
615
+
616
+ `public static function isAbsolute(UriInterface $uri): bool`
617
+
618
+ Whether the URI is absolute, i.e. it has a scheme.
619
+
620
+ ### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
621
+
622
+ `public static function isNetworkPathReference(UriInterface $uri): bool`
623
+
624
+ Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
625
+ termed an network-path reference.
626
+
627
+ ### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
628
+
629
+ `public static function isAbsolutePathReference(UriInterface $uri): bool`
630
+
631
+ Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
632
+ termed an absolute-path reference.
633
+
634
+ ### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
635
+
636
+ `public static function isRelativePathReference(UriInterface $uri): bool`
637
+
638
+ Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
639
+ termed a relative-path reference.
640
+
641
+ ### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
642
+
643
+ `public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
644
+
645
+ Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
646
+ fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
647
+ (apart from its fragment) is considered a same-document reference.
648
+
649
+ ## URI Components
650
+
651
+ Additional methods to work with URI components.
652
+
653
+ ### `GuzzleHttp\Psr7\Uri::isDefaultPort`
654
+
655
+ `public static function isDefaultPort(UriInterface $uri): bool`
656
+
657
+ Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
658
+ or the standard port. This method can be used independently of the implementation.
659
+
660
+ ### `GuzzleHttp\Psr7\Uri::composeComponents`
661
+
662
+ `public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
663
+
664
+ Composes a URI reference string from its various components according to
665
+ [RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
666
+ manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
667
+
668
+ ### `GuzzleHttp\Psr7\Uri::fromParts`
669
+
670
+ `public static function fromParts(array $parts): UriInterface`
671
+
672
+ Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components.
673
+
674
+
675
+ ### `GuzzleHttp\Psr7\Uri::withQueryValue`
676
+
677
+ `public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
678
+
679
+ Creates a new URI with a specific query string value. Any existing query string values that exactly match the
680
+ provided key are removed and replaced with the given key value pair. A value of null will set the query string
681
+ key without a value, e.g. "key" instead of "key=value".
682
+
683
+ ### `GuzzleHttp\Psr7\Uri::withQueryValues`
684
+
685
+ `public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface`
686
+
687
+ Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an
688
+ associative array of key => value.
689
+
690
+ ### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
691
+
692
+ `public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
693
+
694
+ Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
695
+ provided key are removed.
696
+
697
+ ## Reference Resolution
698
+
699
+ `GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
700
+ to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
701
+ do when resolving a link in a website based on the current request URI.
702
+
703
+ ### `GuzzleHttp\Psr7\UriResolver::resolve`
704
+
705
+ `public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
706
+
707
+ Converts the relative URI into a new URI that is resolved against the base URI.
708
+
709
+ ### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
710
+
711
+ `public static function removeDotSegments(string $path): string`
712
+
713
+ Removes dot segments from a path and returns the new path according to
714
+ [RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).
715
+
716
+ ### `GuzzleHttp\Psr7\UriResolver::relativize`
717
+
718
+ `public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
719
+
720
+ Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
721
+
722
+ ```php
723
+ (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
724
+ ```
725
+
726
+ One use-case is to use the current request URI as base URI and then generate relative links in your documents
727
+ to reduce the document size or offer self-contained downloadable document archives.
728
+
729
+ ```php
730
+ $base = new Uri('http://example.com/a/b/');
731
+ echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
732
+ echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
733
+ echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
734
+ echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
735
+ ```
736
+
737
+ ## Normalization and Comparison
738
+
739
+ `GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
740
+ [RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).
741
+
742
+ ### `GuzzleHttp\Psr7\UriNormalizer::normalize`
743
+
744
+ `public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
745
+
746
+ Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
747
+ This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
748
+ of normalizations to apply. The following normalizations are available:
749
+
750
+ - `UriNormalizer::PRESERVING_NORMALIZATIONS`
751
+
752
+ Default normalizations which only include the ones that preserve semantics.
753
+
754
+ - `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
755
+
756
+ All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
757
+
758
+ Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
759
+
760
+ - `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
761
+
762
+ Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
763
+ ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
764
+ not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
765
+ characters by URI normalizers.
766
+
767
+ Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
768
+
769
+ - `UriNormalizer::CONVERT_EMPTY_PATH`
770
+
771
+ Converts the empty path to "/" for http and https URIs.
772
+
773
+ Example: `http://example.org` → `http://example.org/`
774
+
775
+ - `UriNormalizer::REMOVE_DEFAULT_HOST`
776
+
777
+ Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
778
+ "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
779
+ RFC 3986.
780
+
781
+ Example: `file://localhost/myfile` → `file:///myfile`
782
+
783
+ - `UriNormalizer::REMOVE_DEFAULT_PORT`
784
+
785
+ Removes the default port of the given URI scheme from the URI.
786
+
787
+ Example: `http://example.org:80/` → `http://example.org/`
788
+
789
+ - `UriNormalizer::REMOVE_DOT_SEGMENTS`
790
+
791
+ Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
792
+ change the semantics of the URI reference.
793
+
794
+ Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
795
+
796
+ - `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
797
+
798
+ Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
799
+ and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
800
+ may change the semantics. Encoded slashes (%2F) are not removed.
801
+
802
+ Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
803
+
804
+ - `UriNormalizer::SORT_QUERY_PARAMETERS`
805
+
806
+ Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
807
+ significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
808
+ of the URI.
809
+
810
+ Example: `?lang=en&article=fred` → `?article=fred&lang=en`
811
+
812
+ ### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
813
+
814
+ `public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
815
+
816
+ Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
817
+ `$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
818
+ This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
819
+ equivalence or difference of relative references does not mean anything.
820
+
821
+ ## Version Guidance
822
+
823
+ | Version | Status | PHP Version |
824
+ |---------|----------------|------------------|
825
+ | 1.x | Security fixes | >= 5.4, < 8.2 |
826
+ | 2.x | Latest | ^7.2.5 \|\| ^8.0 |
827
+
828
+ ## Security
829
+
830
+ 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.
831
+
832
+ ## License
833
+
834
+ Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
835
+
836
+ ## For Enterprise
837
+
838
+ Available as part of the Tidelift Subscription
839
+
840
+ The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-psr7?utm_source=packagist-guzzlehttp-psr7&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
src/vendor/guzzlehttp/psr7/composer.json ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "guzzlehttp/psr7",
3
+ "description": "PSR-7 message implementation that also provides common utility methods",
4
+ "keywords": [
5
+ "request",
6
+ "response",
7
+ "message",
8
+ "stream",
9
+ "http",
10
+ "uri",
11
+ "url",
12
+ "psr-7"
13
+ ],
14
+ "license": "MIT",
15
+ "authors": [
16
+ {
17
+ "name": "Graham Campbell",
18
+ "email": "hello@gjcampbell.co.uk",
19
+ "homepage": "https://github.com/GrahamCampbell"
20
+ },
21
+ {
22
+ "name": "Michael Dowling",
23
+ "email": "mtdowling@gmail.com",
24
+ "homepage": "https://github.com/mtdowling"
25
+ },
26
+ {
27
+ "name": "George Mponos",
28
+ "email": "gmponos@gmail.com",
29
+ "homepage": "https://github.com/gmponos"
30
+ },
31
+ {
32
+ "name": "Tobias Nyholm",
33
+ "email": "tobias.nyholm@gmail.com",
34
+ "homepage": "https://github.com/Nyholm"
35
+ },
36
+ {
37
+ "name": "Márk Sági-Kazár",
38
+ "email": "mark.sagikazar@gmail.com",
39
+ "homepage": "https://github.com/sagikazarmark"
40
+ },
41
+ {
42
+ "name": "Tobias Schultze",
43
+ "email": "webmaster@tubo-world.de",
44
+ "homepage": "https://github.com/Tobion"
45
+ },
46
+ {
47
+ "name": "Márk Sági-Kazár",
48
+ "email": "mark.sagikazar@gmail.com",
49
+ "homepage": "https://sagikazarmark.hu"
50
+ }
51
+ ],
52
+ "require": {
53
+ "php": "^7.2.5 || ^8.0",
54
+ "psr/http-factory": "^1.0",
55
+ "psr/http-message": "^1.0",
56
+ "ralouphie/getallheaders": "^3.0"
57
+ },
58
+ "provide": {
59
+ "psr/http-factory-implementation": "1.0",
60
+ "psr/http-message-implementation": "1.0"
61
+ },
62
+ "require-dev": {
63
+ "bamarni/composer-bin-plugin": "^1.4.1",
64
+ "http-interop/http-factory-tests": "^0.9",
65
+ "phpunit/phpunit": "^8.5.8 || ^9.3.10"
66
+ },
67
+ "suggest": {
68
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
69
+ },
70
+ "autoload": {
71
+ "psr-4": {
72
+ "GuzzleHttp\\Psr7\\": "src/"
73
+ }
74
+ },
75
+ "autoload-dev": {
76
+ "psr-4": {
77
+ "GuzzleHttp\\Tests\\Psr7\\": "tests/"
78
+ }
79
+ },
80
+ "extra": {
81
+ "branch-alias": {
82
+ "dev-master": "2.3-dev"
83
+ }
84
+ },
85
+ "config": {
86
+ "allow-plugins": {
87
+ "bamarni/composer-bin-plugin": true
88
+ },
89
+ "preferred-install": "dist",
90
+ "sort-packages": true
91
+ }
92
+ }
src/vendor/guzzlehttp/psr7/src/AppendStream.php ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Reads from multiple streams, one after the other.
11
+ *
12
+ * This is a read-only stream decorator.
13
+ */
14
+ final class AppendStream implements StreamInterface
15
+ {
16
+ /** @var StreamInterface[] Streams being decorated */
17
+ private $streams = [];
18
+
19
+ /** @var bool */
20
+ private $seekable = true;
21
+
22
+ /** @var int */
23
+ private $current = 0;
24
+
25
+ /** @var int */
26
+ private $pos = 0;
27
+
28
+ /**
29
+ * @param StreamInterface[] $streams Streams to decorate. Each stream must
30
+ * be readable.
31
+ */
32
+ public function __construct(array $streams = [])
33
+ {
34
+ foreach ($streams as $stream) {
35
+ $this->addStream($stream);
36
+ }
37
+ }
38
+
39
+ public function __toString(): string
40
+ {
41
+ try {
42
+ $this->rewind();
43
+ return $this->getContents();
44
+ } catch (\Throwable $e) {
45
+ if (\PHP_VERSION_ID >= 70400) {
46
+ throw $e;
47
+ }
48
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
49
+ return '';
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Add a stream to the AppendStream
55
+ *
56
+ * @param StreamInterface $stream Stream to append. Must be readable.
57
+ *
58
+ * @throws \InvalidArgumentException if the stream is not readable
59
+ */
60
+ public function addStream(StreamInterface $stream): void
61
+ {
62
+ if (!$stream->isReadable()) {
63
+ throw new \InvalidArgumentException('Each stream must be readable');
64
+ }
65
+
66
+ // The stream is only seekable if all streams are seekable
67
+ if (!$stream->isSeekable()) {
68
+ $this->seekable = false;
69
+ }
70
+
71
+ $this->streams[] = $stream;
72
+ }
73
+
74
+ public function getContents(): string
75
+ {
76
+ return Utils::copyToString($this);
77
+ }
78
+
79
+ /**
80
+ * Closes each attached stream.
81
+ */
82
+ public function close(): void
83
+ {
84
+ $this->pos = $this->current = 0;
85
+ $this->seekable = true;
86
+
87
+ foreach ($this->streams as $stream) {
88
+ $stream->close();
89
+ }
90
+
91
+ $this->streams = [];
92
+ }
93
+
94
+ /**
95
+ * Detaches each attached stream.
96
+ *
97
+ * Returns null as it's not clear which underlying stream resource to return.
98
+ */
99
+ public function detach()
100
+ {
101
+ $this->pos = $this->current = 0;
102
+ $this->seekable = true;
103
+
104
+ foreach ($this->streams as $stream) {
105
+ $stream->detach();
106
+ }
107
+
108
+ $this->streams = [];
109
+
110
+ return null;
111
+ }
112
+
113
+ public function tell(): int
114
+ {
115
+ return $this->pos;
116
+ }
117
+
118
+ /**
119
+ * Tries to calculate the size by adding the size of each stream.
120
+ *
121
+ * If any of the streams do not return a valid number, then the size of the
122
+ * append stream cannot be determined and null is returned.
123
+ */
124
+ public function getSize(): ?int
125
+ {
126
+ $size = 0;
127
+
128
+ foreach ($this->streams as $stream) {
129
+ $s = $stream->getSize();
130
+ if ($s === null) {
131
+ return null;
132
+ }
133
+ $size += $s;
134
+ }
135
+
136
+ return $size;
137
+ }
138
+
139
+ public function eof(): bool
140
+ {
141
+ return !$this->streams ||
142
+ ($this->current >= count($this->streams) - 1 &&
143
+ $this->streams[$this->current]->eof());
144
+ }
145
+
146
+ public function rewind(): void
147
+ {
148
+ $this->seek(0);
149
+ }
150
+
151
+ /**
152
+ * Attempts to seek to the given position. Only supports SEEK_SET.
153
+ */
154
+ public function seek($offset, $whence = SEEK_SET): void
155
+ {
156
+ if (!$this->seekable) {
157
+ throw new \RuntimeException('This AppendStream is not seekable');
158
+ } elseif ($whence !== SEEK_SET) {
159
+ throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
160
+ }
161
+
162
+ $this->pos = $this->current = 0;
163
+
164
+ // Rewind each stream
165
+ foreach ($this->streams as $i => $stream) {
166
+ try {
167
+ $stream->rewind();
168
+ } catch (\Exception $e) {
169
+ throw new \RuntimeException('Unable to seek stream '
170
+ . $i . ' of the AppendStream', 0, $e);
171
+ }
172
+ }
173
+
174
+ // Seek to the actual position by reading from each stream
175
+ while ($this->pos < $offset && !$this->eof()) {
176
+ $result = $this->read(min(8096, $offset - $this->pos));
177
+ if ($result === '') {
178
+ break;
179
+ }
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Reads from all of the appended streams until the length is met or EOF.
185
+ */
186
+ public function read($length): string
187
+ {
188
+ $buffer = '';
189
+ $total = count($this->streams) - 1;
190
+ $remaining = $length;
191
+ $progressToNext = false;
192
+
193
+ while ($remaining > 0) {
194
+
195
+ // Progress to the next stream if needed.
196
+ if ($progressToNext || $this->streams[$this->current]->eof()) {
197
+ $progressToNext = false;
198
+ if ($this->current === $total) {
199
+ break;
200
+ }
201
+ $this->current++;
202
+ }
203
+
204
+ $result = $this->streams[$this->current]->read($remaining);
205
+
206
+ if ($result === '') {
207
+ $progressToNext = true;
208
+ continue;
209
+ }
210
+
211
+ $buffer .= $result;
212
+ $remaining = $length - strlen($buffer);
213
+ }
214
+
215
+ $this->pos += strlen($buffer);
216
+
217
+ return $buffer;
218
+ }
219
+
220
+ public function isReadable(): bool
221
+ {
222
+ return true;
223
+ }
224
+
225
+ public function isWritable(): bool
226
+ {
227
+ return false;
228
+ }
229
+
230
+ public function isSeekable(): bool
231
+ {
232
+ return $this->seekable;
233
+ }
234
+
235
+ public function write($string): int
236
+ {
237
+ throw new \RuntimeException('Cannot write to an AppendStream');
238
+ }
239
+
240
+ /**
241
+ * {@inheritdoc}
242
+ *
243
+ * @return mixed
244
+ */
245
+ public function getMetadata($key = null)
246
+ {
247
+ return $key ? null : [];
248
+ }
249
+ }
src/vendor/guzzlehttp/psr7/src/BufferStream.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Provides a buffer stream that can be written to to fill a buffer, and read
11
+ * from to remove bytes from the buffer.
12
+ *
13
+ * This stream returns a "hwm" metadata value that tells upstream consumers
14
+ * what the configured high water mark of the stream is, or the maximum
15
+ * preferred size of the buffer.
16
+ */
17
+ final class BufferStream implements StreamInterface
18
+ {
19
+ /** @var int */
20
+ private $hwm;
21
+
22
+ /** @var string */
23
+ private $buffer = '';
24
+
25
+ /**
26
+ * @param int $hwm High water mark, representing the preferred maximum
27
+ * buffer size. If the size of the buffer exceeds the high
28
+ * water mark, then calls to write will continue to succeed
29
+ * but will return 0 to inform writers to slow down
30
+ * until the buffer has been drained by reading from it.
31
+ */
32
+ public function __construct(int $hwm = 16384)
33
+ {
34
+ $this->hwm = $hwm;
35
+ }
36
+
37
+ public function __toString(): string
38
+ {
39
+ return $this->getContents();
40
+ }
41
+
42
+ public function getContents(): string
43
+ {
44
+ $buffer = $this->buffer;
45
+ $this->buffer = '';
46
+
47
+ return $buffer;
48
+ }
49
+
50
+ public function close(): void
51
+ {
52
+ $this->buffer = '';
53
+ }
54
+
55
+ public function detach()
56
+ {
57
+ $this->close();
58
+
59
+ return null;
60
+ }
61
+
62
+ public function getSize(): ?int
63
+ {
64
+ return strlen($this->buffer);
65
+ }
66
+
67
+ public function isReadable(): bool
68
+ {
69
+ return true;
70
+ }
71
+
72
+ public function isWritable(): bool
73
+ {
74
+ return true;
75
+ }
76
+
77
+ public function isSeekable(): bool
78
+ {
79
+ return false;
80
+ }
81
+
82
+ public function rewind(): void
83
+ {
84
+ $this->seek(0);
85
+ }
86
+
87
+ public function seek($offset, $whence = SEEK_SET): void
88
+ {
89
+ throw new \RuntimeException('Cannot seek a BufferStream');
90
+ }
91
+
92
+ public function eof(): bool
93
+ {
94
+ return strlen($this->buffer) === 0;
95
+ }
96
+
97
+ public function tell(): int
98
+ {
99
+ throw new \RuntimeException('Cannot determine the position of a BufferStream');
100
+ }
101
+
102
+ /**
103
+ * Reads data from the buffer.
104
+ */
105
+ public function read($length): string
106
+ {
107
+ $currentLength = strlen($this->buffer);
108
+
109
+ if ($length >= $currentLength) {
110
+ // No need to slice the buffer because we don't have enough data.
111
+ $result = $this->buffer;
112
+ $this->buffer = '';
113
+ } else {
114
+ // Slice up the result to provide a subset of the buffer.
115
+ $result = substr($this->buffer, 0, $length);
116
+ $this->buffer = substr($this->buffer, $length);
117
+ }
118
+
119
+ return $result;
120
+ }
121
+
122
+ /**
123
+ * Writes data to the buffer.
124
+ */
125
+ public function write($string): int
126
+ {
127
+ $this->buffer .= $string;
128
+
129
+ if (strlen($this->buffer) >= $this->hwm) {
130
+ return 0;
131
+ }
132
+
133
+ return strlen($string);
134
+ }
135
+
136
+ /**
137
+ * {@inheritdoc}
138
+ *
139
+ * @return mixed
140
+ */
141
+ public function getMetadata($key = null)
142
+ {
143
+ if ($key === 'hwm') {
144
+ return $this->hwm;
145
+ }
146
+
147
+ return $key ? null : [];
148
+ }
149
+ }
src/vendor/guzzlehttp/psr7/src/CachingStream.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Stream decorator that can cache previously read bytes from a sequentially
11
+ * read stream.
12
+ */
13
+ final class CachingStream implements StreamInterface
14
+ {
15
+ use StreamDecoratorTrait;
16
+
17
+ /** @var StreamInterface Stream being wrapped */
18
+ private $remoteStream;
19
+
20
+ /** @var int Number of bytes to skip reading due to a write on the buffer */
21
+ private $skipReadBytes = 0;
22
+
23
+ /**
24
+ * We will treat the buffer object as the body of the stream
25
+ *
26
+ * @param StreamInterface $stream Stream to cache. The cursor is assumed to be at the beginning of the stream.
27
+ * @param StreamInterface $target Optionally specify where data is cached
28
+ */
29
+ public function __construct(
30
+ StreamInterface $stream,
31
+ StreamInterface $target = null
32
+ ) {
33
+ $this->remoteStream = $stream;
34
+ $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+'));
35
+ }
36
+
37
+ public function getSize(): ?int
38
+ {
39
+ $remoteSize = $this->remoteStream->getSize();
40
+
41
+ if (null === $remoteSize) {
42
+ return null;
43
+ }
44
+
45
+ return max($this->stream->getSize(), $remoteSize);
46
+ }
47
+
48
+ public function rewind(): void
49
+ {
50
+ $this->seek(0);
51
+ }
52
+
53
+ public function seek($offset, $whence = SEEK_SET): void
54
+ {
55
+ if ($whence === SEEK_SET) {
56
+ $byte = $offset;
57
+ } elseif ($whence === SEEK_CUR) {
58
+ $byte = $offset + $this->tell();
59
+ } elseif ($whence === SEEK_END) {
60
+ $size = $this->remoteStream->getSize();
61
+ if ($size === null) {
62
+ $size = $this->cacheEntireStream();
63
+ }
64
+ $byte = $size + $offset;
65
+ } else {
66
+ throw new \InvalidArgumentException('Invalid whence');
67
+ }
68
+
69
+ $diff = $byte - $this->stream->getSize();
70
+
71
+ if ($diff > 0) {
72
+ // Read the remoteStream until we have read in at least the amount
73
+ // of bytes requested, or we reach the end of the file.
74
+ while ($diff > 0 && !$this->remoteStream->eof()) {
75
+ $this->read($diff);
76
+ $diff = $byte - $this->stream->getSize();
77
+ }
78
+ } else {
79
+ // We can just do a normal seek since we've already seen this byte.
80
+ $this->stream->seek($byte);
81
+ }
82
+ }
83
+
84
+ public function read($length): string
85
+ {
86
+ // Perform a regular read on any previously read data from the buffer
87
+ $data = $this->stream->read($length);
88
+ $remaining = $length - strlen($data);
89
+
90
+ // More data was requested so read from the remote stream
91
+ if ($remaining) {
92
+ // If data was written to the buffer in a position that would have
93
+ // been filled from the remote stream, then we must skip bytes on
94
+ // the remote stream to emulate overwriting bytes from that
95
+ // position. This mimics the behavior of other PHP stream wrappers.
96
+ $remoteData = $this->remoteStream->read(
97
+ $remaining + $this->skipReadBytes
98
+ );
99
+
100
+ if ($this->skipReadBytes) {
101
+ $len = strlen($remoteData);
102
+ $remoteData = substr($remoteData, $this->skipReadBytes);
103
+ $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
104
+ }
105
+
106
+ $data .= $remoteData;
107
+ $this->stream->write($remoteData);
108
+ }
109
+
110
+ return $data;
111
+ }
112
+
113
+ public function write($string): int
114
+ {
115
+ // When appending to the end of the currently read stream, you'll want
116
+ // to skip bytes from being read from the remote stream to emulate
117
+ // other stream wrappers. Basically replacing bytes of data of a fixed
118
+ // length.
119
+ $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
120
+ if ($overflow > 0) {
121
+ $this->skipReadBytes += $overflow;
122
+ }
123
+
124
+ return $this->stream->write($string);
125
+ }
126
+
127
+ public function eof(): bool
128
+ {
129
+ return $this->stream->eof() && $this->remoteStream->eof();
130
+ }
131
+
132
+ /**
133
+ * Close both the remote stream and buffer stream
134
+ */
135
+ public function close(): void
136
+ {
137
+ $this->remoteStream->close();
138
+ $this->stream->close();
139
+ }
140
+
141
+ private function cacheEntireStream(): int
142
+ {
143
+ $target = new FnStream(['write' => 'strlen']);
144
+ Utils::copyToStream($this, $target);
145
+
146
+ return $this->tell();
147
+ }
148
+ }
src/vendor/guzzlehttp/psr7/src/DroppingStream.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Stream decorator that begins dropping data once the size of the underlying
11
+ * stream becomes too full.
12
+ */
13
+ final class DroppingStream implements StreamInterface
14
+ {
15
+ use StreamDecoratorTrait;
16
+
17
+ /** @var int */
18
+ private $maxLength;
19
+
20
+ /**
21
+ * @param StreamInterface $stream Underlying stream to decorate.
22
+ * @param int $maxLength Maximum size before dropping data.
23
+ */
24
+ public function __construct(StreamInterface $stream, int $maxLength)
25
+ {
26
+ $this->stream = $stream;
27
+ $this->maxLength = $maxLength;
28
+ }
29
+
30
+ public function write($string): int
31
+ {
32
+ $diff = $this->maxLength - $this->stream->getSize();
33
+
34
+ // Begin returning 0 when the underlying stream is too large.
35
+ if ($diff <= 0) {
36
+ return 0;
37
+ }
38
+
39
+ // Write the stream or a subset of the stream if needed.
40
+ if (strlen($string) < $diff) {
41
+ return $this->stream->write($string);
42
+ }
43
+
44
+ return $this->stream->write(substr($string, 0, $diff));
45
+ }
46
+ }
src/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7\Exception;
6
+
7
+ use InvalidArgumentException;
8
+
9
+ /**
10
+ * Exception thrown if a URI cannot be parsed because it's malformed.
11
+ */
12
+ class MalformedUriException extends InvalidArgumentException
13
+ {
14
+ }
src/vendor/guzzlehttp/psr7/src/FnStream.php ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Compose stream implementations based on a hash of functions.
11
+ *
12
+ * Allows for easy testing and extension of a provided stream without needing
13
+ * to create a concrete class for a simple extension point.
14
+ */
15
+ final class FnStream implements StreamInterface
16
+ {
17
+ private const SLOTS = [
18
+ '__toString', 'close', 'detach', 'rewind',
19
+ 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
20
+ 'isReadable', 'read', 'getContents', 'getMetadata'
21
+ ];
22
+
23
+ /** @var array<string, callable> */
24
+ private $methods;
25
+
26
+ /**
27
+ * @param array<string, callable> $methods Hash of method name to a callable.
28
+ */
29
+ public function __construct(array $methods)
30
+ {
31
+ $this->methods = $methods;
32
+
33
+ // Create the functions on the class
34
+ foreach ($methods as $name => $fn) {
35
+ $this->{'_fn_' . $name} = $fn;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Lazily determine which methods are not implemented.
41
+ *
42
+ * @throws \BadMethodCallException
43
+ */
44
+ public function __get(string $name): void
45
+ {
46
+ throw new \BadMethodCallException(str_replace('_fn_', '', $name)
47
+ . '() is not implemented in the FnStream');
48
+ }
49
+
50
+ /**
51
+ * The close method is called on the underlying stream only if possible.
52
+ */
53
+ public function __destruct()
54
+ {
55
+ if (isset($this->_fn_close)) {
56
+ call_user_func($this->_fn_close);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * An unserialize would allow the __destruct to run when the unserialized value goes out of scope.
62
+ *
63
+ * @throws \LogicException
64
+ */
65
+ public function __wakeup(): void
66
+ {
67
+ throw new \LogicException('FnStream should never be unserialized');
68
+ }
69
+
70
+ /**
71
+ * Adds custom functionality to an underlying stream by intercepting
72
+ * specific method calls.
73
+ *
74
+ * @param StreamInterface $stream Stream to decorate
75
+ * @param array<string, callable> $methods Hash of method name to a closure
76
+ *
77
+ * @return FnStream
78
+ */
79
+ public static function decorate(StreamInterface $stream, array $methods)
80
+ {
81
+ // If any of the required methods were not provided, then simply
82
+ // proxy to the decorated stream.
83
+ foreach (array_diff(self::SLOTS, array_keys($methods)) as $diff) {
84
+ /** @var callable $callable */
85
+ $callable = [$stream, $diff];
86
+ $methods[$diff] = $callable;
87
+ }
88
+
89
+ return new self($methods);
90
+ }
91
+
92
+ public function __toString(): string
93
+ {
94
+ try {
95
+ return call_user_func($this->_fn___toString);
96
+ } catch (\Throwable $e) {
97
+ if (\PHP_VERSION_ID >= 70400) {
98
+ throw $e;
99
+ }
100
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
101
+ return '';
102
+ }
103
+ }
104
+
105
+ public function close(): void
106
+ {
107
+ call_user_func($this->_fn_close);
108
+ }
109
+
110
+ public function detach()
111
+ {
112
+ return call_user_func($this->_fn_detach);
113
+ }
114
+
115
+ public function getSize(): ?int
116
+ {
117
+ return call_user_func($this->_fn_getSize);
118
+ }
119
+
120
+ public function tell(): int
121
+ {
122
+ return call_user_func($this->_fn_tell);
123
+ }
124
+
125
+ public function eof(): bool
126
+ {
127
+ return call_user_func($this->_fn_eof);
128
+ }
129
+
130
+ public function isSeekable(): bool
131
+ {
132
+ return call_user_func($this->_fn_isSeekable);
133
+ }
134
+
135
+ public function rewind(): void
136
+ {
137
+ call_user_func($this->_fn_rewind);
138
+ }
139
+
140
+ public function seek($offset, $whence = SEEK_SET): void
141
+ {
142
+ call_user_func($this->_fn_seek, $offset, $whence);
143
+ }
144
+
145
+ public function isWritable(): bool
146
+ {
147
+ return call_user_func($this->_fn_isWritable);
148
+ }
149
+
150
+ public function write($string): int
151
+ {
152
+ return call_user_func($this->_fn_write, $string);
153
+ }
154
+
155
+ public function isReadable(): bool
156
+ {
157
+ return call_user_func($this->_fn_isReadable);
158
+ }
159
+
160
+ public function read($length): string
161
+ {
162
+ return call_user_func($this->_fn_read, $length);
163
+ }
164
+
165
+ public function getContents(): string
166
+ {
167
+ return call_user_func($this->_fn_getContents);
168
+ }
169
+
170
+ /**
171
+ * {@inheritdoc}
172
+ *
173
+ * @return mixed
174
+ */
175
+ public function getMetadata($key = null)
176
+ {
177
+ return call_user_func($this->_fn_getMetadata, $key);
178
+ }
179
+ }
src/vendor/guzzlehttp/psr7/src/Header.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ final class Header
8
+ {
9
+ /**
10
+ * Parse an array of header values containing ";" separated data into an
11
+ * array of associative arrays representing the header key value pair data
12
+ * of the header. When a parameter does not contain a value, but just
13
+ * contains a key, this function will inject a key with a '' string value.
14
+ *
15
+ * @param string|array $header Header to parse into components.
16
+ */
17
+ public static function parse($header): array
18
+ {
19
+ static $trimmed = "\"' \n\t\r";
20
+ $params = $matches = [];
21
+
22
+ foreach ((array) $header as $value) {
23
+ foreach (self::splitList($value) as $val) {
24
+ $part = [];
25
+ foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
26
+ if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
27
+ $m = $matches[0];
28
+ if (isset($m[1])) {
29
+ $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
30
+ } else {
31
+ $part[] = trim($m[0], $trimmed);
32
+ }
33
+ }
34
+ }
35
+ if ($part) {
36
+ $params[] = $part;
37
+ }
38
+ }
39
+ }
40
+
41
+ return $params;
42
+ }
43
+
44
+ /**
45
+ * Converts an array of header values that may contain comma separated
46
+ * headers into an array of headers with no comma separated values.
47
+ *
48
+ * @param string|array $header Header to normalize.
49
+ *
50
+ * @deprecated Use self::splitList() instead.
51
+ */
52
+ public static function normalize($header): array
53
+ {
54
+ $result = [];
55
+ foreach ((array) $header as $value) {
56
+ foreach (self::splitList($value) as $parsed) {
57
+ $result[] = $parsed;
58
+ }
59
+ }
60
+
61
+ return $result;
62
+ }
63
+
64
+ /**
65
+ * Splits a HTTP header defined to contain comma-separated list into
66
+ * each individual value. Empty values will be removed.
67
+ *
68
+ * Example headers include 'accept', 'cache-control' and 'if-none-match'.
69
+ *
70
+ * This method must not be used to parse headers that are not defined as
71
+ * a list, such as 'user-agent' or 'set-cookie'.
72
+ *
73
+ * @param string|string[] $values Header value as returned by MessageInterface::getHeader()
74
+ *
75
+ * @return string[]
76
+ */
77
+ public static function splitList($values): array
78
+ {
79
+ if (!\is_array($values)) {
80
+ $values = [$values];
81
+ }
82
+
83
+ $result = [];
84
+ foreach ($values as $value) {
85
+ if (!\is_string($value)) {
86
+ throw new \TypeError('$header must either be a string or an array containing strings.');
87
+ }
88
+
89
+ $v = '';
90
+ $isQuoted = false;
91
+ $isEscaped = false;
92
+ for ($i = 0, $max = \strlen($value); $i < $max; $i++) {
93
+ if ($isEscaped) {
94
+ $v .= $value[$i];
95
+ $isEscaped = false;
96
+
97
+ continue;
98
+ }
99
+
100
+ if (!$isQuoted && $value[$i] === ',') {
101
+ $v = \trim($v);
102
+ if ($v !== '') {
103
+ $result[] = $v;
104
+ }
105
+
106
+ $v = '';
107
+ continue;
108
+ }
109
+
110
+ if ($isQuoted && $value[$i] === '\\') {
111
+ $isEscaped = true;
112
+ $v .= $value[$i];
113
+
114
+ continue;
115
+ }
116
+ if ($value[$i] === '"') {
117
+ $isQuoted = !$isQuoted;
118
+ $v .= $value[$i];
119
+
120
+ continue;
121
+ }
122
+
123
+ $v .= $value[$i];
124
+ }
125
+
126
+ $v = \trim($v);
127
+ if ($v !== '') {
128
+ $result[] = $v;
129
+ }
130
+ }
131
+
132
+ return $result;
133
+ }
134
+ }
src/vendor/guzzlehttp/psr7/src/HttpFactory.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\RequestFactoryInterface;
8
+ use Psr\Http\Message\RequestInterface;
9
+ use Psr\Http\Message\ResponseFactoryInterface;
10
+ use Psr\Http\Message\ResponseInterface;
11
+ use Psr\Http\Message\ServerRequestFactoryInterface;
12
+ use Psr\Http\Message\ServerRequestInterface;
13
+ use Psr\Http\Message\StreamFactoryInterface;
14
+ use Psr\Http\Message\StreamInterface;
15
+ use Psr\Http\Message\UploadedFileFactoryInterface;
16
+ use Psr\Http\Message\UploadedFileInterface;
17
+ use Psr\Http\Message\UriFactoryInterface;
18
+ use Psr\Http\Message\UriInterface;
19
+
20
+ /**
21
+ * Implements all of the PSR-17 interfaces.
22
+ *
23
+ * Note: in consuming code it is recommended to require the implemented interfaces
24
+ * and inject the instance of this class multiple times.
25
+ */
26
+ final class HttpFactory implements
27
+ RequestFactoryInterface,
28
+ ResponseFactoryInterface,
29
+ ServerRequestFactoryInterface,
30
+ StreamFactoryInterface,
31
+ UploadedFileFactoryInterface,
32
+ UriFactoryInterface
33
+ {
34
+ public function createUploadedFile(
35
+ StreamInterface $stream,
36
+ int $size = null,
37
+ int $error = \UPLOAD_ERR_OK,
38
+ string $clientFilename = null,
39
+ string $clientMediaType = null
40
+ ): UploadedFileInterface {
41
+ if ($size === null) {
42
+ $size = $stream->getSize();
43
+ }
44
+
45
+ return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
46
+ }
47
+
48
+ public function createStream(string $content = ''): StreamInterface
49
+ {
50
+ return Utils::streamFor($content);
51
+ }
52
+
53
+ public function createStreamFromFile(string $file, string $mode = 'r'): StreamInterface
54
+ {
55
+ try {
56
+ $resource = Utils::tryFopen($file, $mode);
57
+ } catch (\RuntimeException $e) {
58
+ if ('' === $mode || false === \in_array($mode[0], ['r', 'w', 'a', 'x', 'c'], true)) {
59
+ throw new \InvalidArgumentException(sprintf('Invalid file opening mode "%s"', $mode), 0, $e);
60
+ }
61
+
62
+ throw $e;
63
+ }
64
+
65
+ return Utils::streamFor($resource);
66
+ }
67
+
68
+ public function createStreamFromResource($resource): StreamInterface
69
+ {
70
+ return Utils::streamFor($resource);
71
+ }
72
+
73
+ public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
74
+ {
75
+ if (empty($method)) {
76
+ if (!empty($serverParams['REQUEST_METHOD'])) {
77
+ $method = $serverParams['REQUEST_METHOD'];
78
+ } else {
79
+ throw new \InvalidArgumentException('Cannot determine HTTP method');
80
+ }
81
+ }
82
+
83
+ return new ServerRequest($method, $uri, [], null, '1.1', $serverParams);
84
+ }
85
+
86
+ public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
87
+ {
88
+ return new Response($code, [], null, '1.1', $reasonPhrase);
89
+ }
90
+
91
+ public function createRequest(string $method, $uri): RequestInterface
92
+ {
93
+ return new Request($method, $uri);
94
+ }
95
+
96
+ public function createUri(string $uri = ''): UriInterface
97
+ {
98
+ return new Uri($uri);
99
+ }
100
+ }
src/vendor/guzzlehttp/psr7/src/InflateStream.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.
11
+ *
12
+ * This stream decorator converts the provided stream to a PHP stream resource,
13
+ * then appends the zlib.inflate filter. The stream is then converted back
14
+ * to a Guzzle stream resource to be used as a Guzzle stream.
15
+ *
16
+ * @link http://tools.ietf.org/html/rfc1950
17
+ * @link http://tools.ietf.org/html/rfc1952
18
+ * @link http://php.net/manual/en/filters.compression.php
19
+ */
20
+ final class InflateStream implements StreamInterface
21
+ {
22
+ use StreamDecoratorTrait;
23
+
24
+ public function __construct(StreamInterface $stream)
25
+ {
26
+ $resource = StreamWrapper::getResource($stream);
27
+ // Specify window=15+32, so zlib will use header detection to both gzip (with header) and zlib data
28
+ // See http://www.zlib.net/manual.html#Advanced definition of inflateInit2
29
+ // "Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
30
+ // Default window size is 15.
31
+ stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15 + 32]);
32
+ $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource));
33
+ }
34
+ }
src/vendor/guzzlehttp/psr7/src/LazyOpenStream.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Lazily reads or writes to a file that is opened only after an IO operation
11
+ * take place on the stream.
12
+ */
13
+ final class LazyOpenStream implements StreamInterface
14
+ {
15
+ use StreamDecoratorTrait;
16
+
17
+ /** @var string */
18
+ private $filename;
19
+
20
+ /** @var string */
21
+ private $mode;
22
+
23
+ /**
24
+ * @param string $filename File to lazily open
25
+ * @param string $mode fopen mode to use when opening the stream
26
+ */
27
+ public function __construct(string $filename, string $mode)
28
+ {
29
+ $this->filename = $filename;
30
+ $this->mode = $mode;
31
+ }
32
+
33
+ /**
34
+ * Creates the underlying stream lazily when required.
35
+ */
36
+ protected function createStream(): StreamInterface
37
+ {
38
+ return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode));
39
+ }
40
+ }
src/vendor/guzzlehttp/psr7/src/LimitStream.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Decorator used to return only a subset of a stream.
11
+ */
12
+ final class LimitStream implements StreamInterface
13
+ {
14
+ use StreamDecoratorTrait;
15
+
16
+ /** @var int Offset to start reading from */
17
+ private $offset;
18
+
19
+ /** @var int Limit the number of bytes that can be read */
20
+ private $limit;
21
+
22
+ /**
23
+ * @param StreamInterface $stream Stream to wrap
24
+ * @param int $limit Total number of bytes to allow to be read
25
+ * from the stream. Pass -1 for no limit.
26
+ * @param int $offset Position to seek to before reading (only
27
+ * works on seekable streams).
28
+ */
29
+ public function __construct(
30
+ StreamInterface $stream,
31
+ int $limit = -1,
32
+ int $offset = 0
33
+ ) {
34
+ $this->stream = $stream;
35
+ $this->setLimit($limit);
36
+ $this->setOffset($offset);
37
+ }
38
+
39
+ public function eof(): bool
40
+ {
41
+ // Always return true if the underlying stream is EOF
42
+ if ($this->stream->eof()) {
43
+ return true;
44
+ }
45
+
46
+ // No limit and the underlying stream is not at EOF
47
+ if ($this->limit === -1) {
48
+ return false;
49
+ }
50
+
51
+ return $this->stream->tell() >= $this->offset + $this->limit;
52
+ }
53
+
54
+ /**
55
+ * Returns the size of the limited subset of data
56
+ */
57
+ public function getSize(): ?int
58
+ {
59
+ if (null === ($length = $this->stream->getSize())) {
60
+ return null;
61
+ } elseif ($this->limit === -1) {
62
+ return $length - $this->offset;
63
+ } else {
64
+ return min($this->limit, $length - $this->offset);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Allow for a bounded seek on the read limited stream
70
+ */
71
+ public function seek($offset, $whence = SEEK_SET): void
72
+ {
73
+ if ($whence !== SEEK_SET || $offset < 0) {
74
+ throw new \RuntimeException(sprintf(
75
+ 'Cannot seek to offset %s with whence %s',
76
+ $offset,
77
+ $whence
78
+ ));
79
+ }
80
+
81
+ $offset += $this->offset;
82
+
83
+ if ($this->limit !== -1) {
84
+ if ($offset > $this->offset + $this->limit) {
85
+ $offset = $this->offset + $this->limit;
86
+ }
87
+ }
88
+
89
+ $this->stream->seek($offset);
90
+ }
91
+
92
+ /**
93
+ * Give a relative tell()
94
+ */
95
+ public function tell(): int
96
+ {
97
+ return $this->stream->tell() - $this->offset;
98
+ }
99
+
100
+ /**
101
+ * Set the offset to start limiting from
102
+ *
103
+ * @param int $offset Offset to seek to and begin byte limiting from
104
+ *
105
+ * @throws \RuntimeException if the stream cannot be seeked.
106
+ */
107
+ public function setOffset(int $offset): void
108
+ {
109
+ $current = $this->stream->tell();
110
+
111
+ if ($current !== $offset) {
112
+ // If the stream cannot seek to the offset position, then read to it
113
+ if ($this->stream->isSeekable()) {
114
+ $this->stream->seek($offset);
115
+ } elseif ($current > $offset) {
116
+ throw new \RuntimeException("Could not seek to stream offset $offset");
117
+ } else {
118
+ $this->stream->read($offset - $current);
119
+ }
120
+ }
121
+
122
+ $this->offset = $offset;
123
+ }
124
+
125
+ /**
126
+ * Set the limit of bytes that the decorator allows to be read from the
127
+ * stream.
128
+ *
129
+ * @param int $limit Number of bytes to allow to be read from the stream.
130
+ * Use -1 for no limit.
131
+ */
132
+ public function setLimit(int $limit): void
133
+ {
134
+ $this->limit = $limit;
135
+ }
136
+
137
+ public function read($length): string
138
+ {
139
+ if ($this->limit === -1) {
140
+ return $this->stream->read($length);
141
+ }
142
+
143
+ // Check if the current position is less than the total allowed
144
+ // bytes + original offset
145
+ $remaining = ($this->offset + $this->limit) - $this->stream->tell();
146
+ if ($remaining > 0) {
147
+ // Only return the amount of requested data, ensuring that the byte
148
+ // limit is not exceeded
149
+ return $this->stream->read(min($remaining, $length));
150
+ }
151
+
152
+ return '';
153
+ }
154
+ }
src/vendor/guzzlehttp/psr7/src/Message.php ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\MessageInterface;
8
+ use Psr\Http\Message\RequestInterface;
9
+ use Psr\Http\Message\ResponseInterface;
10
+
11
+ final class Message
12
+ {
13
+ /**
14
+ * Returns the string representation of an HTTP message.
15
+ *
16
+ * @param MessageInterface $message Message to convert to a string.
17
+ */
18
+ public static function toString(MessageInterface $message): string
19
+ {
20
+ if ($message instanceof RequestInterface) {
21
+ $msg = trim($message->getMethod() . ' '
22
+ . $message->getRequestTarget())
23
+ . ' HTTP/' . $message->getProtocolVersion();
24
+ if (!$message->hasHeader('host')) {
25
+ $msg .= "\r\nHost: " . $message->getUri()->getHost();
26
+ }
27
+ } elseif ($message instanceof ResponseInterface) {
28
+ $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
29
+ . $message->getStatusCode() . ' '
30
+ . $message->getReasonPhrase();
31
+ } else {
32
+ throw new \InvalidArgumentException('Unknown message type');
33
+ }
34
+
35
+ foreach ($message->getHeaders() as $name => $values) {
36
+ if (strtolower($name) === 'set-cookie') {
37
+ foreach ($values as $value) {
38
+ $msg .= "\r\n{$name}: " . $value;
39
+ }
40
+ } else {
41
+ $msg .= "\r\n{$name}: " . implode(', ', $values);
42
+ }
43
+ }
44
+
45
+ return "{$msg}\r\n\r\n" . $message->getBody();
46
+ }
47
+
48
+ /**
49
+ * Get a short summary of the message body.
50
+ *
51
+ * Will return `null` if the response is not printable.
52
+ *
53
+ * @param MessageInterface $message The message to get the body summary
54
+ * @param int $truncateAt The maximum allowed size of the summary
55
+ */
56
+ public static function bodySummary(MessageInterface $message, int $truncateAt = 120): ?string
57
+ {
58
+ $body = $message->getBody();
59
+
60
+ if (!$body->isSeekable() || !$body->isReadable()) {
61
+ return null;
62
+ }
63
+
64
+ $size = $body->getSize();
65
+
66
+ if ($size === 0) {
67
+ return null;
68
+ }
69
+
70
+ $summary = $body->read($truncateAt);
71
+ $body->rewind();
72
+
73
+ if ($size > $truncateAt) {
74
+ $summary .= ' (truncated...)';
75
+ }
76
+
77
+ // Matches any printable character, including unicode characters:
78
+ // letters, marks, numbers, punctuation, spacing, and separators.
79
+ if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary)) {
80
+ return null;
81
+ }
82
+
83
+ return $summary;
84
+ }
85
+
86
+ /**
87
+ * Attempts to rewind a message body and throws an exception on failure.
88
+ *
89
+ * The body of the message will only be rewound if a call to `tell()`
90
+ * returns a value other than `0`.
91
+ *
92
+ * @param MessageInterface $message Message to rewind
93
+ *
94
+ * @throws \RuntimeException
95
+ */
96
+ public static function rewindBody(MessageInterface $message): void
97
+ {
98
+ $body = $message->getBody();
99
+
100
+ if ($body->tell()) {
101
+ $body->rewind();
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Parses an HTTP message into an associative array.
107
+ *
108
+ * The array contains the "start-line" key containing the start line of
109
+ * the message, "headers" key containing an associative array of header
110
+ * array values, and a "body" key containing the body of the message.
111
+ *
112
+ * @param string $message HTTP request or response to parse.
113
+ */
114
+ public static function parseMessage(string $message): array
115
+ {
116
+ if (!$message) {
117
+ throw new \InvalidArgumentException('Invalid message');
118
+ }
119
+
120
+ $message = ltrim($message, "\r\n");
121
+
122
+ $messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
123
+
124
+ if ($messageParts === false || count($messageParts) !== 2) {
125
+ throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
126
+ }
127
+
128
+ [$rawHeaders, $body] = $messageParts;
129
+ $rawHeaders .= "\r\n"; // Put back the delimiter we split previously
130
+ $headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
131
+
132
+ if ($headerParts === false || count($headerParts) !== 2) {
133
+ throw new \InvalidArgumentException('Invalid message: Missing status line');
134
+ }
135
+
136
+ [$startLine, $rawHeaders] = $headerParts;
137
+
138
+ if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
139
+ // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
140
+ $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
141
+ }
142
+
143
+ /** @var array[] $headerLines */
144
+ $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
145
+
146
+ // If these aren't the same, then one line didn't match and there's an invalid header.
147
+ if ($count !== substr_count($rawHeaders, "\n")) {
148
+ // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4
149
+ if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
150
+ throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
151
+ }
152
+
153
+ throw new \InvalidArgumentException('Invalid header syntax');
154
+ }
155
+
156
+ $headers = [];
157
+
158
+ foreach ($headerLines as $headerLine) {
159
+ $headers[$headerLine[1]][] = $headerLine[2];
160
+ }
161
+
162
+ return [
163
+ 'start-line' => $startLine,
164
+ 'headers' => $headers,
165
+ 'body' => $body,
166
+ ];
167
+ }
168
+
169
+ /**
170
+ * Constructs a URI for an HTTP request message.
171
+ *
172
+ * @param string $path Path from the start-line
173
+ * @param array $headers Array of headers (each value an array).
174
+ */
175
+ public static function parseRequestUri(string $path, array $headers): string
176
+ {
177
+ $hostKey = array_filter(array_keys($headers), function ($k) {
178
+ // Numeric array keys are converted to int by PHP.
179
+ $k = (string) $k;
180
+
181
+ return strtolower($k) === 'host';
182
+ });
183
+
184
+ // If no host is found, then a full URI cannot be constructed.
185
+ if (!$hostKey) {
186
+ return $path;
187
+ }
188
+
189
+ $host = $headers[reset($hostKey)][0];
190
+ $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
191
+
192
+ return $scheme . '://' . $host . '/' . ltrim($path, '/');
193
+ }
194
+
195
+ /**
196
+ * Parses a request message string into a request object.
197
+ *
198
+ * @param string $message Request message string.
199
+ */
200
+ public static function parseRequest(string $message): RequestInterface
201
+ {
202
+ $data = self::parseMessage($message);
203
+ $matches = [];
204
+ if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
205
+ throw new \InvalidArgumentException('Invalid request string');
206
+ }
207
+ $parts = explode(' ', $data['start-line'], 3);
208
+ $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
209
+
210
+ $request = new Request(
211
+ $parts[0],
212
+ $matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1],
213
+ $data['headers'],
214
+ $data['body'],
215
+ $version
216
+ );
217
+
218
+ return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
219
+ }
220
+
221
+ /**
222
+ * Parses a response message string into a response object.
223
+ *
224
+ * @param string $message Response message string.
225
+ */
226
+ public static function parseResponse(string $message): ResponseInterface
227
+ {
228
+ $data = self::parseMessage($message);
229
+ // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
230
+ // between status-code and reason-phrase is required. But browsers accept
231
+ // responses without space and reason as well.
232
+ if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
233
+ throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']);
234
+ }
235
+ $parts = explode(' ', $data['start-line'], 3);
236
+
237
+ return new Response(
238
+ (int) $parts[1],
239
+ $data['headers'],
240
+ $data['body'],
241
+ explode('/', $parts[0])[1],
242
+ $parts[2] ?? null
243
+ );
244
+ }
245
+ }
src/vendor/guzzlehttp/psr7/src/MessageTrait.php ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\MessageInterface;
8
+ use Psr\Http\Message\StreamInterface;
9
+
10
+ /**
11
+ * Trait implementing functionality common to requests and responses.
12
+ */
13
+ trait MessageTrait
14
+ {
15
+ /** @var array<string, string[]> Map of all registered headers, as original name => array of values */
16
+ private $headers = [];
17
+
18
+ /** @var array<string, string> Map of lowercase header name => original name at registration */
19
+ private $headerNames = [];
20
+
21
+ /** @var string */
22
+ private $protocol = '1.1';
23
+
24
+ /** @var StreamInterface|null */
25
+ private $stream;
26
+
27
+ public function getProtocolVersion(): string
28
+ {
29
+ return $this->protocol;
30
+ }
31
+
32
+ public function withProtocolVersion($version): MessageInterface
33
+ {
34
+ if ($this->protocol === $version) {
35
+ return $this;
36
+ }
37
+
38
+ $new = clone $this;
39
+ $new->protocol = $version;
40
+ return $new;
41
+ }
42
+
43
+ public function getHeaders(): array
44
+ {
45
+ return $this->headers;
46
+ }
47
+
48
+ public function hasHeader($header): bool
49
+ {
50
+ return isset($this->headerNames[strtolower($header)]);
51
+ }
52
+
53
+ public function getHeader($header): array
54
+ {
55
+ $header = strtolower($header);
56
+
57
+ if (!isset($this->headerNames[$header])) {
58
+ return [];
59
+ }
60
+
61
+ $header = $this->headerNames[$header];
62
+
63
+ return $this->headers[$header];
64
+ }
65
+
66
+ public function getHeaderLine($header): string
67
+ {
68
+ return implode(', ', $this->getHeader($header));
69
+ }
70
+
71
+ public function withHeader($header, $value): MessageInterface
72
+ {
73
+ $this->assertHeader($header);
74
+ $value = $this->normalizeHeaderValue($value);
75
+ $normalized = strtolower($header);
76
+
77
+ $new = clone $this;
78
+ if (isset($new->headerNames[$normalized])) {
79
+ unset($new->headers[$new->headerNames[$normalized]]);
80
+ }
81
+ $new->headerNames[$normalized] = $header;
82
+ $new->headers[$header] = $value;
83
+
84
+ return $new;
85
+ }
86
+
87
+ public function withAddedHeader($header, $value): MessageInterface
88
+ {
89
+ $this->assertHeader($header);
90
+ $value = $this->normalizeHeaderValue($value);
91
+ $normalized = strtolower($header);
92
+
93
+ $new = clone $this;
94
+ if (isset($new->headerNames[$normalized])) {
95
+ $header = $this->headerNames[$normalized];
96
+ $new->headers[$header] = array_merge($this->headers[$header], $value);
97
+ } else {
98
+ $new->headerNames[$normalized] = $header;
99
+ $new->headers[$header] = $value;
100
+ }
101
+
102
+ return $new;
103
+ }
104
+
105
+ public function withoutHeader($header): MessageInterface
106
+ {
107
+ $normalized = strtolower($header);
108
+
109
+ if (!isset($this->headerNames[$normalized])) {
110
+ return $this;
111
+ }
112
+
113
+ $header = $this->headerNames[$normalized];
114
+
115
+ $new = clone $this;
116
+ unset($new->headers[$header], $new->headerNames[$normalized]);
117
+
118
+ return $new;
119
+ }
120
+
121
+ public function getBody(): StreamInterface
122
+ {
123
+ if (!$this->stream) {
124
+ $this->stream = Utils::streamFor('');
125
+ }
126
+
127
+ return $this->stream;
128
+ }
129
+
130
+ public function withBody(StreamInterface $body): MessageInterface
131
+ {
132
+ if ($body === $this->stream) {
133
+ return $this;
134
+ }
135
+
136
+ $new = clone $this;
137
+ $new->stream = $body;
138
+ return $new;
139
+ }
140
+
141
+ /**
142
+ * @param array<string|int, string|string[]> $headers
143
+ */
144
+ private function setHeaders(array $headers): void
145
+ {
146
+ $this->headerNames = $this->headers = [];
147
+ foreach ($headers as $header => $value) {
148
+ // Numeric array keys are converted to int by PHP.
149
+ $header = (string) $header;
150
+
151
+ $this->assertHeader($header);
152
+ $value = $this->normalizeHeaderValue($value);
153
+ $normalized = strtolower($header);
154
+ if (isset($this->headerNames[$normalized])) {
155
+ $header = $this->headerNames[$normalized];
156
+ $this->headers[$header] = array_merge($this->headers[$header], $value);
157
+ } else {
158
+ $this->headerNames[$normalized] = $header;
159
+ $this->headers[$header] = $value;
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * @param mixed $value
166
+ *
167
+ * @return string[]
168
+ */
169
+ private function normalizeHeaderValue($value): array
170
+ {
171
+ if (!is_array($value)) {
172
+ return $this->trimAndValidateHeaderValues([$value]);
173
+ }
174
+
175
+ if (count($value) === 0) {
176
+ throw new \InvalidArgumentException('Header value can not be an empty array.');
177
+ }
178
+
179
+ return $this->trimAndValidateHeaderValues($value);
180
+ }
181
+
182
+ /**
183
+ * Trims whitespace from the header values.
184
+ *
185
+ * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
186
+ *
187
+ * header-field = field-name ":" OWS field-value OWS
188
+ * OWS = *( SP / HTAB )
189
+ *
190
+ * @param mixed[] $values Header values
191
+ *
192
+ * @return string[] Trimmed header values
193
+ *
194
+ * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
195
+ */
196
+ private function trimAndValidateHeaderValues(array $values): array
197
+ {
198
+ return array_map(function ($value) {
199
+ if (!is_scalar($value) && null !== $value) {
200
+ throw new \InvalidArgumentException(sprintf(
201
+ 'Header value must be scalar or null but %s provided.',
202
+ is_object($value) ? get_class($value) : gettype($value)
203
+ ));
204
+ }
205
+
206
+ $trimmed = trim((string) $value, " \t");
207
+ $this->assertValue($trimmed);
208
+
209
+ return $trimmed;
210
+ }, array_values($values));
211
+ }
212
+
213
+ /**
214
+ * @see https://tools.ietf.org/html/rfc7230#section-3.2
215
+ *
216
+ * @param mixed $header
217
+ */
218
+ private function assertHeader($header): void
219
+ {
220
+ if (!is_string($header)) {
221
+ throw new \InvalidArgumentException(sprintf(
222
+ 'Header name must be a string but %s provided.',
223
+ is_object($header) ? get_class($header) : gettype($header)
224
+ ));
225
+ }
226
+
227
+ if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) {
228
+ throw new \InvalidArgumentException(
229
+ sprintf(
230
+ '"%s" is not valid header name',
231
+ $header
232
+ )
233
+ );
234
+ }
235
+ }
236
+
237
+ /**
238
+ * @see https://tools.ietf.org/html/rfc7230#section-3.2
239
+ *
240
+ * field-value = *( field-content / obs-fold )
241
+ * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
242
+ * field-vchar = VCHAR / obs-text
243
+ * VCHAR = %x21-7E
244
+ * obs-text = %x80-FF
245
+ * obs-fold = CRLF 1*( SP / HTAB )
246
+ */
247
+ private function assertValue(string $value): void
248
+ {
249
+ // The regular expression intentionally does not support the obs-fold production, because as
250
+ // per RFC 7230#3.2.4:
251
+ //
252
+ // A sender MUST NOT generate a message that includes
253
+ // line folding (i.e., that has any field-value that contains a match to
254
+ // the obs-fold rule) unless the message is intended for packaging
255
+ // within the message/http media type.
256
+ //
257
+ // Clients must not send a request with line folding and a server sending folded headers is
258
+ // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
259
+ // folding is not likely to break any legitimate use case.
260
+ if (! preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/', $value)) {
261
+ throw new \InvalidArgumentException(sprintf('"%s" is not valid header value', $value));
262
+ }
263
+ }
264
+ }
src/vendor/guzzlehttp/psr7/src/MimeType.php ADDED
@@ -0,0 +1,1237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ final class MimeType
8
+ {
9
+ private const MIME_TYPES = [
10
+ '1km' => 'application/vnd.1000minds.decision-model+xml',
11
+ '3dml' => 'text/vnd.in3d.3dml',
12
+ '3ds' => 'image/x-3ds',
13
+ '3g2' => 'video/3gpp2',
14
+ '3gp' => 'video/3gp',
15
+ '3gpp' => 'video/3gpp',
16
+ '3mf' => 'model/3mf',
17
+ '7z' => 'application/x-7z-compressed',
18
+ '7zip' => 'application/x-7z-compressed',
19
+ '123' => 'application/vnd.lotus-1-2-3',
20
+ 'aab' => 'application/x-authorware-bin',
21
+ 'aac' => 'audio/x-acc',
22
+ 'aam' => 'application/x-authorware-map',
23
+ 'aas' => 'application/x-authorware-seg',
24
+ 'abw' => 'application/x-abiword',
25
+ 'ac' => 'application/vnd.nokia.n-gage.ac+xml',
26
+ 'ac3' => 'audio/ac3',
27
+ 'acc' => 'application/vnd.americandynamics.acc',
28
+ 'ace' => 'application/x-ace-compressed',
29
+ 'acu' => 'application/vnd.acucobol',
30
+ 'acutc' => 'application/vnd.acucorp',
31
+ 'adp' => 'audio/adpcm',
32
+ 'aep' => 'application/vnd.audiograph',
33
+ 'afm' => 'application/x-font-type1',
34
+ 'afp' => 'application/vnd.ibm.modcap',
35
+ 'age' => 'application/vnd.age',
36
+ 'ahead' => 'application/vnd.ahead.space',
37
+ 'ai' => 'application/pdf',
38
+ 'aif' => 'audio/x-aiff',
39
+ 'aifc' => 'audio/x-aiff',
40
+ 'aiff' => 'audio/x-aiff',
41
+ 'air' => 'application/vnd.adobe.air-application-installer-package+zip',
42
+ 'ait' => 'application/vnd.dvb.ait',
43
+ 'ami' => 'application/vnd.amiga.ami',
44
+ 'amr' => 'audio/amr',
45
+ 'apk' => 'application/vnd.android.package-archive',
46
+ 'apng' => 'image/apng',
47
+ 'appcache' => 'text/cache-manifest',
48
+ 'application' => 'application/x-ms-application',
49
+ 'apr' => 'application/vnd.lotus-approach',
50
+ 'arc' => 'application/x-freearc',
51
+ 'arj' => 'application/x-arj',
52
+ 'asc' => 'application/pgp-signature',
53
+ 'asf' => 'video/x-ms-asf',
54
+ 'asm' => 'text/x-asm',
55
+ 'aso' => 'application/vnd.accpac.simply.aso',
56
+ 'asx' => 'video/x-ms-asf',
57
+ 'atc' => 'application/vnd.acucorp',
58
+ 'atom' => 'application/atom+xml',
59
+ 'atomcat' => 'application/atomcat+xml',
60
+ 'atomdeleted' => 'application/atomdeleted+xml',
61
+ 'atomsvc' => 'application/atomsvc+xml',
62
+ 'atx' => 'application/vnd.antix.game-component',
63
+ 'au' => 'audio/x-au',
64
+ 'avci' => 'image/avci',
65
+ 'avcs' => 'image/avcs',
66
+ 'avi' => 'video/x-msvideo',
67
+ 'avif' => 'image/avif',
68
+ 'aw' => 'application/applixware',
69
+ 'azf' => 'application/vnd.airzip.filesecure.azf',
70
+ 'azs' => 'application/vnd.airzip.filesecure.azs',
71
+ 'azv' => 'image/vnd.airzip.accelerator.azv',
72
+ 'azw' => 'application/vnd.amazon.ebook',
73
+ 'b16' => 'image/vnd.pco.b16',
74
+ 'bat' => 'application/x-msdownload',
75
+ 'bcpio' => 'application/x-bcpio',
76
+ 'bdf' => 'application/x-font-bdf',
77
+ 'bdm' => 'application/vnd.syncml.dm+wbxml',
78
+ 'bdoc' => 'application/x-bdoc',
79
+ 'bed' => 'application/vnd.realvnc.bed',
80
+ 'bh2' => 'application/vnd.fujitsu.oasysprs',
81
+ 'bin' => 'application/octet-stream',
82
+ 'blb' => 'application/x-blorb',
83
+ 'blorb' => 'application/x-blorb',
84
+ 'bmi' => 'application/vnd.bmi',
85
+ 'bmml' => 'application/vnd.balsamiq.bmml+xml',
86
+ 'bmp' => 'image/bmp',
87
+ 'book' => 'application/vnd.framemaker',
88
+ 'box' => 'application/vnd.previewsystems.box',
89
+ 'boz' => 'application/x-bzip2',
90
+ 'bpk' => 'application/octet-stream',
91
+ 'bpmn' => 'application/octet-stream',
92
+ 'bsp' => 'model/vnd.valve.source.compiled-map',
93
+ 'btif' => 'image/prs.btif',
94
+ 'buffer' => 'application/octet-stream',
95
+ 'bz' => 'application/x-bzip',
96
+ 'bz2' => 'application/x-bzip2',
97
+ 'c' => 'text/x-c',
98
+ 'c4d' => 'application/vnd.clonk.c4group',
99
+ 'c4f' => 'application/vnd.clonk.c4group',
100
+ 'c4g' => 'application/vnd.clonk.c4group',
101
+ 'c4p' => 'application/vnd.clonk.c4group',
102
+ 'c4u' => 'application/vnd.clonk.c4group',
103
+ 'c11amc' => 'application/vnd.cluetrust.cartomobile-config',
104
+ 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg',
105
+ 'cab' => 'application/vnd.ms-cab-compressed',
106
+ 'caf' => 'audio/x-caf',
107
+ 'cap' => 'application/vnd.tcpdump.pcap',
108
+ 'car' => 'application/vnd.curl.car',
109
+ 'cat' => 'application/vnd.ms-pki.seccat',
110
+ 'cb7' => 'application/x-cbr',
111
+ 'cba' => 'application/x-cbr',
112
+ 'cbr' => 'application/x-cbr',
113
+ 'cbt' => 'application/x-cbr',
114
+ 'cbz' => 'application/x-cbr',
115
+ 'cc' => 'text/x-c',
116
+ 'cco' => 'application/x-cocoa',
117
+ 'cct' => 'application/x-director',
118
+ 'ccxml' => 'application/ccxml+xml',
119
+ 'cdbcmsg' => 'application/vnd.contact.cmsg',
120
+ 'cdf' => 'application/x-netcdf',
121
+ 'cdfx' => 'application/cdfx+xml',
122
+ 'cdkey' => 'application/vnd.mediastation.cdkey',
123
+ 'cdmia' => 'application/cdmi-capability',
124
+ 'cdmic' => 'application/cdmi-container',
125
+ 'cdmid' => 'application/cdmi-domain',
126
+ 'cdmio' => 'application/cdmi-object',
127
+ 'cdmiq' => 'application/cdmi-queue',
128
+ 'cdr' => 'application/cdr',
129
+ 'cdx' => 'chemical/x-cdx',
130
+ 'cdxml' => 'application/vnd.chemdraw+xml',
131
+ 'cdy' => 'application/vnd.cinderella',
132
+ 'cer' => 'application/pkix-cert',
133
+ 'cfs' => 'application/x-cfs-compressed',
134
+ 'cgm' => 'image/cgm',
135
+ 'chat' => 'application/x-chat',
136
+ 'chm' => 'application/vnd.ms-htmlhelp',
137
+ 'chrt' => 'application/vnd.kde.kchart',
138
+ 'cif' => 'chemical/x-cif',
139
+ 'cii' => 'application/vnd.anser-web-certificate-issue-initiation',
140
+ 'cil' => 'application/vnd.ms-artgalry',
141
+ 'cjs' => 'application/node',
142
+ 'cla' => 'application/vnd.claymore',
143
+ 'class' => 'application/octet-stream',
144
+ 'clkk' => 'application/vnd.crick.clicker.keyboard',
145
+ 'clkp' => 'application/vnd.crick.clicker.palette',
146
+ 'clkt' => 'application/vnd.crick.clicker.template',
147
+ 'clkw' => 'application/vnd.crick.clicker.wordbank',
148
+ 'clkx' => 'application/vnd.crick.clicker',
149
+ 'clp' => 'application/x-msclip',
150
+ 'cmc' => 'application/vnd.cosmocaller',
151
+ 'cmdf' => 'chemical/x-cmdf',
152
+ 'cml' => 'chemical/x-cml',
153
+ 'cmp' => 'application/vnd.yellowriver-custom-menu',
154
+ 'cmx' => 'image/x-cmx',
155
+ 'cod' => 'application/vnd.rim.cod',
156
+ 'coffee' => 'text/coffeescript',
157
+ 'com' => 'application/x-msdownload',
158
+ 'conf' => 'text/plain',
159
+ 'cpio' => 'application/x-cpio',
160
+ 'cpl' => 'application/cpl+xml',
161
+ 'cpp' => 'text/x-c',
162
+ 'cpt' => 'application/mac-compactpro',
163
+ 'crd' => 'application/x-mscardfile',
164
+ 'crl' => 'application/pkix-crl',
165
+ 'crt' => 'application/x-x509-ca-cert',
166
+ 'crx' => 'application/x-chrome-extension',
167
+ 'cryptonote' => 'application/vnd.rig.cryptonote',
168
+ 'csh' => 'application/x-csh',
169
+ 'csl' => 'application/vnd.citationstyles.style+xml',
170
+ 'csml' => 'chemical/x-csml',
171
+ 'csp' => 'application/vnd.commonspace',
172
+ 'csr' => 'application/octet-stream',
173
+ 'css' => 'text/css',
174
+ 'cst' => 'application/x-director',
175
+ 'csv' => 'text/csv',
176
+ 'cu' => 'application/cu-seeme',
177
+ 'curl' => 'text/vnd.curl',
178
+ 'cww' => 'application/prs.cww',
179
+ 'cxt' => 'application/x-director',
180
+ 'cxx' => 'text/x-c',
181
+ 'dae' => 'model/vnd.collada+xml',
182
+ 'daf' => 'application/vnd.mobius.daf',
183
+ 'dart' => 'application/vnd.dart',
184
+ 'dataless' => 'application/vnd.fdsn.seed',
185
+ 'davmount' => 'application/davmount+xml',
186
+ 'dbf' => 'application/vnd.dbf',
187
+ 'dbk' => 'application/docbook+xml',
188
+ 'dcr' => 'application/x-director',
189
+ 'dcurl' => 'text/vnd.curl.dcurl',
190
+ 'dd2' => 'application/vnd.oma.dd2+xml',
191
+ 'ddd' => 'application/vnd.fujixerox.ddd',
192
+ 'ddf' => 'application/vnd.syncml.dmddf+xml',
193
+ 'dds' => 'image/vnd.ms-dds',
194
+ 'deb' => 'application/x-debian-package',
195
+ 'def' => 'text/plain',
196
+ 'deploy' => 'application/octet-stream',
197
+ 'der' => 'application/x-x509-ca-cert',
198
+ 'dfac' => 'application/vnd.dreamfactory',
199
+ 'dgc' => 'application/x-dgc-compressed',
200
+ 'dic' => 'text/x-c',
201
+ 'dir' => 'application/x-director',
202
+ 'dis' => 'application/vnd.mobius.dis',
203
+ 'disposition-notification' => 'message/disposition-notification',
204
+ 'dist' => 'application/octet-stream',
205
+ 'distz' => 'application/octet-stream',
206
+ 'djv' => 'image/vnd.djvu',
207
+ 'djvu' => 'image/vnd.djvu',
208
+ 'dll' => 'application/octet-stream',
209
+ 'dmg' => 'application/x-apple-diskimage',
210
+ 'dmn' => 'application/octet-stream',
211
+ 'dmp' => 'application/vnd.tcpdump.pcap',
212
+ 'dms' => 'application/octet-stream',
213
+ 'dna' => 'application/vnd.dna',
214
+ 'doc' => 'application/msword',
215
+ 'docm' => 'application/vnd.ms-word.template.macroEnabled.12',
216
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
217
+ 'dot' => 'application/msword',
218
+ 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
219
+ 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
220
+ 'dp' => 'application/vnd.osgi.dp',
221
+ 'dpg' => 'application/vnd.dpgraph',
222
+ 'dra' => 'audio/vnd.dra',
223
+ 'drle' => 'image/dicom-rle',
224
+ 'dsc' => 'text/prs.lines.tag',
225
+ 'dssc' => 'application/dssc+der',
226
+ 'dtb' => 'application/x-dtbook+xml',
227
+ 'dtd' => 'application/xml-dtd',
228
+ 'dts' => 'audio/vnd.dts',
229
+ 'dtshd' => 'audio/vnd.dts.hd',
230
+ 'dump' => 'application/octet-stream',
231
+ 'dvb' => 'video/vnd.dvb.file',
232
+ 'dvi' => 'application/x-dvi',
233
+ 'dwd' => 'application/atsc-dwd+xml',
234
+ 'dwf' => 'model/vnd.dwf',
235
+ 'dwg' => 'image/vnd.dwg',
236
+ 'dxf' => 'image/vnd.dxf',
237
+ 'dxp' => 'application/vnd.spotfire.dxp',
238
+ 'dxr' => 'application/x-director',
239
+ 'ear' => 'application/java-archive',
240
+ 'ecelp4800' => 'audio/vnd.nuera.ecelp4800',
241
+ 'ecelp7470' => 'audio/vnd.nuera.ecelp7470',
242
+ 'ecelp9600' => 'audio/vnd.nuera.ecelp9600',
243
+ 'ecma' => 'application/ecmascript',
244
+ 'edm' => 'application/vnd.novadigm.edm',
245
+ 'edx' => 'application/vnd.novadigm.edx',
246
+ 'efif' => 'application/vnd.picsel',
247
+ 'ei6' => 'application/vnd.pg.osasli',
248
+ 'elc' => 'application/octet-stream',
249
+ 'emf' => 'image/emf',
250
+ 'eml' => 'message/rfc822',
251
+ 'emma' => 'application/emma+xml',
252
+ 'emotionml' => 'application/emotionml+xml',
253
+ 'emz' => 'application/x-msmetafile',
254
+ 'eol' => 'audio/vnd.digital-winds',
255
+ 'eot' => 'application/vnd.ms-fontobject',
256
+ 'eps' => 'application/postscript',
257
+ 'epub' => 'application/epub+zip',
258
+ 'es' => 'application/ecmascript',
259
+ 'es3' => 'application/vnd.eszigno3+xml',
260
+ 'esa' => 'application/vnd.osgi.subsystem',
261
+ 'esf' => 'application/vnd.epson.esf',
262
+ 'et3' => 'application/vnd.eszigno3+xml',
263
+ 'etx' => 'text/x-setext',
264
+ 'eva' => 'application/x-eva',
265
+ 'evy' => 'application/x-envoy',
266
+ 'exe' => 'application/octet-stream',
267
+ 'exi' => 'application/exi',
268
+ 'exp' => 'application/express',
269
+ 'exr' => 'image/aces',
270
+ 'ext' => 'application/vnd.novadigm.ext',
271
+ 'ez' => 'application/andrew-inset',
272
+ 'ez2' => 'application/vnd.ezpix-album',
273
+ 'ez3' => 'application/vnd.ezpix-package',
274
+ 'f' => 'text/x-fortran',
275
+ 'f4v' => 'video/mp4',
276
+ 'f77' => 'text/x-fortran',
277
+ 'f90' => 'text/x-fortran',
278
+ 'fbs' => 'image/vnd.fastbidsheet',
279
+ 'fcdt' => 'application/vnd.adobe.formscentral.fcdt',
280
+ 'fcs' => 'application/vnd.isac.fcs',
281
+ 'fdf' => 'application/vnd.fdf',
282
+ 'fdt' => 'application/fdt+xml',
283
+ 'fe_launch' => 'application/vnd.denovo.fcselayout-link',
284
+ 'fg5' => 'application/vnd.fujitsu.oasysgp',
285
+ 'fgd' => 'application/x-director',
286
+ 'fh' => 'image/x-freehand',
287
+ 'fh4' => 'image/x-freehand',
288
+ 'fh5' => 'image/x-freehand',
289
+ 'fh7' => 'image/x-freehand',
290
+ 'fhc' => 'image/x-freehand',
291
+ 'fig' => 'application/x-xfig',
292
+ 'fits' => 'image/fits',
293
+ 'flac' => 'audio/x-flac',
294
+ 'fli' => 'video/x-fli',
295
+ 'flo' => 'application/vnd.micrografx.flo',
296
+ 'flv' => 'video/x-flv',
297
+ 'flw' => 'application/vnd.kde.kivio',
298
+ 'flx' => 'text/vnd.fmi.flexstor',
299
+ 'fly' => 'text/vnd.fly',
300
+ 'fm' => 'application/vnd.framemaker',
301
+ 'fnc' => 'application/vnd.frogans.fnc',
302
+ 'fo' => 'application/vnd.software602.filler.form+xml',
303
+ 'for' => 'text/x-fortran',
304
+ 'fpx' => 'image/vnd.fpx',
305
+ 'frame' => 'application/vnd.framemaker',
306
+ 'fsc' => 'application/vnd.fsc.weblaunch',
307
+ 'fst' => 'image/vnd.fst',
308
+ 'ftc' => 'application/vnd.fluxtime.clip',
309
+ 'fti' => 'application/vnd.anser-web-funds-transfer-initiation',
310
+ 'fvt' => 'video/vnd.fvt',
311
+ 'fxp' => 'application/vnd.adobe.fxp',
312
+ 'fxpl' => 'application/vnd.adobe.fxp',
313
+ 'fzs' => 'application/vnd.fuzzysheet',
314
+ 'g2w' => 'application/vnd.geoplan',
315
+ 'g3' => 'image/g3fax',
316
+ 'g3w' => 'application/vnd.geospace',
317
+ 'gac' => 'application/vnd.groove-account',
318
+ 'gam' => 'application/x-tads',
319
+ 'gbr' => 'application/rpki-ghostbusters',
320
+ 'gca' => 'application/x-gca-compressed',
321
+ 'gdl' => 'model/vnd.gdl',
322
+ 'gdoc' => 'application/vnd.google-apps.document',
323
+ 'ged' => 'text/vnd.familysearch.gedcom',
324
+ 'geo' => 'application/vnd.dynageo',
325
+ 'geojson' => 'application/geo+json',
326
+ 'gex' => 'application/vnd.geometry-explorer',
327
+ 'ggb' => 'application/vnd.geogebra.file',
328
+ 'ggt' => 'application/vnd.geogebra.tool',
329
+ 'ghf' => 'application/vnd.groove-help',
330
+ 'gif' => 'image/gif',
331
+ 'gim' => 'application/vnd.groove-identity-message',
332
+ 'glb' => 'model/gltf-binary',
333
+ 'gltf' => 'model/gltf+json',
334
+ 'gml' => 'application/gml+xml',
335
+ 'gmx' => 'application/vnd.gmx',
336
+ 'gnumeric' => 'application/x-gnumeric',
337
+ 'gpg' => 'application/gpg-keys',
338
+ 'gph' => 'application/vnd.flographit',
339
+ 'gpx' => 'application/gpx+xml',
340
+ 'gqf' => 'application/vnd.grafeq',
341
+ 'gqs' => 'application/vnd.grafeq',
342
+ 'gram' => 'application/srgs',
343
+ 'gramps' => 'application/x-gramps-xml',
344
+ 'gre' => 'application/vnd.geometry-explorer',
345
+ 'grv' => 'application/vnd.groove-injector',
346
+ 'grxml' => 'application/srgs+xml',
347
+ 'gsf' => 'application/x-font-ghostscript',
348
+ 'gsheet' => 'application/vnd.google-apps.spreadsheet',
349
+ 'gslides' => 'application/vnd.google-apps.presentation',
350
+ 'gtar' => 'application/x-gtar',
351
+ 'gtm' => 'application/vnd.groove-tool-message',
352
+ 'gtw' => 'model/vnd.gtw',
353
+ 'gv' => 'text/vnd.graphviz',
354
+ 'gxf' => 'application/gxf',
355
+ 'gxt' => 'application/vnd.geonext',
356
+ 'gz' => 'application/gzip',
357
+ 'gzip' => 'application/gzip',
358
+ 'h' => 'text/x-c',
359
+ 'h261' => 'video/h261',
360
+ 'h263' => 'video/h263',
361
+ 'h264' => 'video/h264',
362
+ 'hal' => 'application/vnd.hal+xml',
363
+ 'hbci' => 'application/vnd.hbci',
364
+ 'hbs' => 'text/x-handlebars-template',
365
+ 'hdd' => 'application/x-virtualbox-hdd',
366
+ 'hdf' => 'application/x-hdf',
367
+ 'heic' => 'image/heic',
368
+ 'heics' => 'image/heic-sequence',
369
+ 'heif' => 'image/heif',
370
+ 'heifs' => 'image/heif-sequence',
371
+ 'hej2' => 'image/hej2k',
372
+ 'held' => 'application/atsc-held+xml',
373
+ 'hh' => 'text/x-c',
374
+ 'hjson' => 'application/hjson',
375
+ 'hlp' => 'application/winhlp',
376
+ 'hpgl' => 'application/vnd.hp-hpgl',
377
+ 'hpid' => 'application/vnd.hp-hpid',
378
+ 'hps' => 'application/vnd.hp-hps',
379
+ 'hqx' => 'application/mac-binhex40',
380
+ 'hsj2' => 'image/hsj2',
381
+ 'htc' => 'text/x-component',
382
+ 'htke' => 'application/vnd.kenameaapp',
383
+ 'htm' => 'text/html',
384
+ 'html' => 'text/html',
385
+ 'hvd' => 'application/vnd.yamaha.hv-dic',
386
+ 'hvp' => 'application/vnd.yamaha.hv-voice',
387
+ 'hvs' => 'application/vnd.yamaha.hv-script',
388
+ 'i2g' => 'application/vnd.intergeo',
389
+ 'icc' => 'application/vnd.iccprofile',
390
+ 'ice' => 'x-conference/x-cooltalk',
391
+ 'icm' => 'application/vnd.iccprofile',
392
+ 'ico' => 'image/x-icon',
393
+ 'ics' => 'text/calendar',
394
+ 'ief' => 'image/ief',
395
+ 'ifb' => 'text/calendar',
396
+ 'ifm' => 'application/vnd.shana.informed.formdata',
397
+ 'iges' => 'model/iges',
398
+ 'igl' => 'application/vnd.igloader',
399
+ 'igm' => 'application/vnd.insors.igm',
400
+ 'igs' => 'model/iges',
401
+ 'igx' => 'application/vnd.micrografx.igx',
402
+ 'iif' => 'application/vnd.shana.informed.interchange',
403
+ 'img' => 'application/octet-stream',
404
+ 'imp' => 'application/vnd.accpac.simply.imp',
405
+ 'ims' => 'application/vnd.ms-ims',
406
+ 'in' => 'text/plain',
407
+ 'ini' => 'text/plain',
408
+ 'ink' => 'application/inkml+xml',
409
+ 'inkml' => 'application/inkml+xml',
410
+ 'install' => 'application/x-install-instructions',
411
+ 'iota' => 'application/vnd.astraea-software.iota',
412
+ 'ipfix' => 'application/ipfix',
413
+ 'ipk' => 'application/vnd.shana.informed.package',
414
+ 'irm' => 'application/vnd.ibm.rights-management',
415
+ 'irp' => 'application/vnd.irepository.package+xml',
416
+ 'iso' => 'application/x-iso9660-image',
417
+ 'itp' => 'application/vnd.shana.informed.formtemplate',
418
+ 'its' => 'application/its+xml',
419
+ 'ivp' => 'application/vnd.immervision-ivp',
420
+ 'ivu' => 'application/vnd.immervision-ivu',
421
+ 'jad' => 'text/vnd.sun.j2me.app-descriptor',
422
+ 'jade' => 'text/jade',
423
+ 'jam' => 'application/vnd.jam',
424
+ 'jar' => 'application/java-archive',
425
+ 'jardiff' => 'application/x-java-archive-diff',
426
+ 'java' => 'text/x-java-source',
427
+ 'jhc' => 'image/jphc',
428
+ 'jisp' => 'application/vnd.jisp',
429
+ 'jls' => 'image/jls',
430
+ 'jlt' => 'application/vnd.hp-jlyt',
431
+ 'jng' => 'image/x-jng',
432
+ 'jnlp' => 'application/x-java-jnlp-file',
433
+ 'joda' => 'application/vnd.joost.joda-archive',
434
+ 'jp2' => 'image/jp2',
435
+ 'jpe' => 'image/jpeg',
436
+ 'jpeg' => 'image/jpeg',
437
+ 'jpf' => 'image/jpx',
438
+ 'jpg' => 'image/jpeg',
439
+ 'jpg2' => 'image/jp2',
440
+ 'jpgm' => 'video/jpm',
441
+ 'jpgv' => 'video/jpeg',
442
+ 'jph' => 'image/jph',
443
+ 'jpm' => 'video/jpm',
444
+ 'jpx' => 'image/jpx',
445
+ 'js' => 'application/javascript',
446
+ 'json' => 'application/json',
447
+ 'json5' => 'application/json5',
448
+ 'jsonld' => 'application/ld+json',
449
+ 'jsonml' => 'application/jsonml+json',
450
+ 'jsx' => 'text/jsx',
451
+ 'jxr' => 'image/jxr',
452
+ 'jxra' => 'image/jxra',
453
+ 'jxrs' => 'image/jxrs',
454
+ 'jxs' => 'image/jxs',
455
+ 'jxsc' => 'image/jxsc',
456
+ 'jxsi' => 'image/jxsi',
457
+ 'jxss' => 'image/jxss',
458
+ 'kar' => 'audio/midi',
459
+ 'karbon' => 'application/vnd.kde.karbon',
460
+ 'kdb' => 'application/octet-stream',
461
+ 'kdbx' => 'application/x-keepass2',
462
+ 'key' => 'application/x-iwork-keynote-sffkey',
463
+ 'kfo' => 'application/vnd.kde.kformula',
464
+ 'kia' => 'application/vnd.kidspiration',
465
+ 'kml' => 'application/vnd.google-earth.kml+xml',
466
+ 'kmz' => 'application/vnd.google-earth.kmz',
467
+ 'kne' => 'application/vnd.kinar',
468
+ 'knp' => 'application/vnd.kinar',
469
+ 'kon' => 'application/vnd.kde.kontour',
470
+ 'kpr' => 'application/vnd.kde.kpresenter',
471
+ 'kpt' => 'application/vnd.kde.kpresenter',
472
+ 'kpxx' => 'application/vnd.ds-keypoint',
473
+ 'ksp' => 'application/vnd.kde.kspread',
474
+ 'ktr' => 'application/vnd.kahootz',
475
+ 'ktx' => 'image/ktx',
476
+ 'ktx2' => 'image/ktx2',
477
+ 'ktz' => 'application/vnd.kahootz',
478
+ 'kwd' => 'application/vnd.kde.kword',
479
+ 'kwt' => 'application/vnd.kde.kword',
480
+ 'lasxml' => 'application/vnd.las.las+xml',
481
+ 'latex' => 'application/x-latex',
482
+ 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',
483
+ 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
484
+ 'les' => 'application/vnd.hhe.lesson-player',
485
+ 'less' => 'text/less',
486
+ 'lgr' => 'application/lgr+xml',
487
+ 'lha' => 'application/octet-stream',
488
+ 'link66' => 'application/vnd.route66.link66+xml',
489
+ 'list' => 'text/plain',
490
+ 'list3820' => 'application/vnd.ibm.modcap',
491
+ 'listafp' => 'application/vnd.ibm.modcap',
492
+ 'litcoffee' => 'text/coffeescript',
493
+ 'lnk' => 'application/x-ms-shortcut',
494
+ 'log' => 'text/plain',
495
+ 'lostxml' => 'application/lost+xml',
496
+ 'lrf' => 'application/octet-stream',
497
+ 'lrm' => 'application/vnd.ms-lrm',
498
+ 'ltf' => 'application/vnd.frogans.ltf',
499
+ 'lua' => 'text/x-lua',
500
+ 'luac' => 'application/x-lua-bytecode',
501
+ 'lvp' => 'audio/vnd.lucent.voice',
502
+ 'lwp' => 'application/vnd.lotus-wordpro',
503
+ 'lzh' => 'application/octet-stream',
504
+ 'm1v' => 'video/mpeg',
505
+ 'm2a' => 'audio/mpeg',
506
+ 'm2v' => 'video/mpeg',
507
+ 'm3a' => 'audio/mpeg',
508
+ 'm3u' => 'text/plain',
509
+ 'm3u8' => 'application/vnd.apple.mpegurl',
510
+ 'm4a' => 'audio/x-m4a',
511
+ 'm4p' => 'application/mp4',
512
+ 'm4s' => 'video/iso.segment',
513
+ 'm4u' => 'application/vnd.mpegurl',
514
+ 'm4v' => 'video/x-m4v',
515
+ 'm13' => 'application/x-msmediaview',
516
+ 'm14' => 'application/x-msmediaview',
517
+ 'm21' => 'application/mp21',
518
+ 'ma' => 'application/mathematica',
519
+ 'mads' => 'application/mads+xml',
520
+ 'maei' => 'application/mmt-aei+xml',
521
+ 'mag' => 'application/vnd.ecowin.chart',
522
+ 'maker' => 'application/vnd.framemaker',
523
+ 'man' => 'text/troff',
524
+ 'manifest' => 'text/cache-manifest',
525
+ 'map' => 'application/json',
526
+ 'mar' => 'application/octet-stream',
527
+ 'markdown' => 'text/markdown',
528
+ 'mathml' => 'application/mathml+xml',
529
+ 'mb' => 'application/mathematica',
530
+ 'mbk' => 'application/vnd.mobius.mbk',
531
+ 'mbox' => 'application/mbox',
532
+ 'mc1' => 'application/vnd.medcalcdata',
533
+ 'mcd' => 'application/vnd.mcd',
534
+ 'mcurl' => 'text/vnd.curl.mcurl',
535
+ 'md' => 'text/markdown',
536
+ 'mdb' => 'application/x-msaccess',
537
+ 'mdi' => 'image/vnd.ms-modi',
538
+ 'mdx' => 'text/mdx',
539
+ 'me' => 'text/troff',
540
+ 'mesh' => 'model/mesh',
541
+ 'meta4' => 'application/metalink4+xml',
542
+ 'metalink' => 'application/metalink+xml',
543
+ 'mets' => 'application/mets+xml',
544
+ 'mfm' => 'application/vnd.mfmp',
545
+ 'mft' => 'application/rpki-manifest',
546
+ 'mgp' => 'application/vnd.osgeo.mapguide.package',
547
+ 'mgz' => 'application/vnd.proteus.magazine',
548
+ 'mid' => 'audio/midi',
549
+ 'midi' => 'audio/midi',
550
+ 'mie' => 'application/x-mie',
551
+ 'mif' => 'application/vnd.mif',
552
+ 'mime' => 'message/rfc822',
553
+ 'mj2' => 'video/mj2',
554
+ 'mjp2' => 'video/mj2',
555
+ 'mjs' => 'application/javascript',
556
+ 'mk3d' => 'video/x-matroska',
557
+ 'mka' => 'audio/x-matroska',
558
+ 'mkd' => 'text/x-markdown',
559
+ 'mks' => 'video/x-matroska',
560
+ 'mkv' => 'video/x-matroska',
561
+ 'mlp' => 'application/vnd.dolby.mlp',
562
+ 'mmd' => 'application/vnd.chipnuts.karaoke-mmd',
563
+ 'mmf' => 'application/vnd.smaf',
564
+ 'mml' => 'text/mathml',
565
+ 'mmr' => 'image/vnd.fujixerox.edmics-mmr',
566
+ 'mng' => 'video/x-mng',
567
+ 'mny' => 'application/x-msmoney',
568
+ 'mobi' => 'application/x-mobipocket-ebook',
569
+ 'mods' => 'application/mods+xml',
570
+ 'mov' => 'video/quicktime',
571
+ 'movie' => 'video/x-sgi-movie',
572
+ 'mp2' => 'audio/mpeg',
573
+ 'mp2a' => 'audio/mpeg',
574
+ 'mp3' => 'audio/mpeg',
575
+ 'mp4' => 'video/mp4',
576
+ 'mp4a' => 'audio/mp4',
577
+ 'mp4s' => 'application/mp4',
578
+ 'mp4v' => 'video/mp4',
579
+ 'mp21' => 'application/mp21',
580
+ 'mpc' => 'application/vnd.mophun.certificate',
581
+ 'mpd' => 'application/dash+xml',
582
+ 'mpe' => 'video/mpeg',
583
+ 'mpeg' => 'video/mpeg',
584
+ 'mpf' => 'application/media-policy-dataset+xml',
585
+ 'mpg' => 'video/mpeg',
586
+ 'mpg4' => 'video/mp4',
587
+ 'mpga' => 'audio/mpeg',
588
+ 'mpkg' => 'application/vnd.apple.installer+xml',
589
+ 'mpm' => 'application/vnd.blueice.multipass',
590
+ 'mpn' => 'application/vnd.mophun.application',
591
+ 'mpp' => 'application/vnd.ms-project',
592
+ 'mpt' => 'application/vnd.ms-project',
593
+ 'mpy' => 'application/vnd.ibm.minipay',
594
+ 'mqy' => 'application/vnd.mobius.mqy',
595
+ 'mrc' => 'application/marc',
596
+ 'mrcx' => 'application/marcxml+xml',
597
+ 'ms' => 'text/troff',
598
+ 'mscml' => 'application/mediaservercontrol+xml',
599
+ 'mseed' => 'application/vnd.fdsn.mseed',
600
+ 'mseq' => 'application/vnd.mseq',
601
+ 'msf' => 'application/vnd.epson.msf',
602
+ 'msg' => 'application/vnd.ms-outlook',
603
+ 'msh' => 'model/mesh',
604
+ 'msi' => 'application/x-msdownload',
605
+ 'msl' => 'application/vnd.mobius.msl',
606
+ 'msm' => 'application/octet-stream',
607
+ 'msp' => 'application/octet-stream',
608
+ 'msty' => 'application/vnd.muvee.style',
609
+ 'mtl' => 'model/mtl',
610
+ 'mts' => 'model/vnd.mts',
611
+ 'mus' => 'application/vnd.musician',
612
+ 'musd' => 'application/mmt-usd+xml',
613
+ 'musicxml' => 'application/vnd.recordare.musicxml+xml',
614
+ 'mvb' => 'application/x-msmediaview',
615
+ 'mvt' => 'application/vnd.mapbox-vector-tile',
616
+ 'mwf' => 'application/vnd.mfer',
617
+ 'mxf' => 'application/mxf',
618
+ 'mxl' => 'application/vnd.recordare.musicxml',
619
+ 'mxmf' => 'audio/mobile-xmf',
620
+ 'mxml' => 'application/xv+xml',
621
+ 'mxs' => 'application/vnd.triscape.mxs',
622
+ 'mxu' => 'video/vnd.mpegurl',
623
+ 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',
624
+ 'n3' => 'text/n3',
625
+ 'nb' => 'application/mathematica',
626
+ 'nbp' => 'application/vnd.wolfram.player',
627
+ 'nc' => 'application/x-netcdf',
628
+ 'ncx' => 'application/x-dtbncx+xml',
629
+ 'nfo' => 'text/x-nfo',
630
+ 'ngdat' => 'application/vnd.nokia.n-gage.data',
631
+ 'nitf' => 'application/vnd.nitf',
632
+ 'nlu' => 'application/vnd.neurolanguage.nlu',
633
+ 'nml' => 'application/vnd.enliven',
634
+ 'nnd' => 'application/vnd.noblenet-directory',
635
+ 'nns' => 'application/vnd.noblenet-sealer',
636
+ 'nnw' => 'application/vnd.noblenet-web',
637
+ 'npx' => 'image/vnd.net-fpx',
638
+ 'nq' => 'application/n-quads',
639
+ 'nsc' => 'application/x-conference',
640
+ 'nsf' => 'application/vnd.lotus-notes',
641
+ 'nt' => 'application/n-triples',
642
+ 'ntf' => 'application/vnd.nitf',
643
+ 'numbers' => 'application/x-iwork-numbers-sffnumbers',
644
+ 'nzb' => 'application/x-nzb',
645
+ 'oa2' => 'application/vnd.fujitsu.oasys2',
646
+ 'oa3' => 'application/vnd.fujitsu.oasys3',
647
+ 'oas' => 'application/vnd.fujitsu.oasys',
648
+ 'obd' => 'application/x-msbinder',
649
+ 'obgx' => 'application/vnd.openblox.game+xml',
650
+ 'obj' => 'model/obj',
651
+ 'oda' => 'application/oda',
652
+ 'odb' => 'application/vnd.oasis.opendocument.database',
653
+ 'odc' => 'application/vnd.oasis.opendocument.chart',
654
+ 'odf' => 'application/vnd.oasis.opendocument.formula',
655
+ 'odft' => 'application/vnd.oasis.opendocument.formula-template',
656
+ 'odg' => 'application/vnd.oasis.opendocument.graphics',
657
+ 'odi' => 'application/vnd.oasis.opendocument.image',
658
+ 'odm' => 'application/vnd.oasis.opendocument.text-master',
659
+ 'odp' => 'application/vnd.oasis.opendocument.presentation',
660
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
661
+ 'odt' => 'application/vnd.oasis.opendocument.text',
662
+ 'oga' => 'audio/ogg',
663
+ 'ogex' => 'model/vnd.opengex',
664
+ 'ogg' => 'audio/ogg',
665
+ 'ogv' => 'video/ogg',
666
+ 'ogx' => 'application/ogg',
667
+ 'omdoc' => 'application/omdoc+xml',
668
+ 'onepkg' => 'application/onenote',
669
+ 'onetmp' => 'application/onenote',
670
+ 'onetoc' => 'application/onenote',
671
+ 'onetoc2' => 'application/onenote',
672
+ 'opf' => 'application/oebps-package+xml',
673
+ 'opml' => 'text/x-opml',
674
+ 'oprc' => 'application/vnd.palm',
675
+ 'opus' => 'audio/ogg',
676
+ 'org' => 'text/x-org',
677
+ 'osf' => 'application/vnd.yamaha.openscoreformat',
678
+ 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
679
+ 'osm' => 'application/vnd.openstreetmap.data+xml',
680
+ 'otc' => 'application/vnd.oasis.opendocument.chart-template',
681
+ 'otf' => 'font/otf',
682
+ 'otg' => 'application/vnd.oasis.opendocument.graphics-template',
683
+ 'oth' => 'application/vnd.oasis.opendocument.text-web',
684
+ 'oti' => 'application/vnd.oasis.opendocument.image-template',
685
+ 'otp' => 'application/vnd.oasis.opendocument.presentation-template',
686
+ 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
687
+ 'ott' => 'application/vnd.oasis.opendocument.text-template',
688
+ 'ova' => 'application/x-virtualbox-ova',
689
+ 'ovf' => 'application/x-virtualbox-ovf',
690
+ 'owl' => 'application/rdf+xml',
691
+ 'oxps' => 'application/oxps',
692
+ 'oxt' => 'application/vnd.openofficeorg.extension',
693
+ 'p' => 'text/x-pascal',
694
+ 'p7a' => 'application/x-pkcs7-signature',
695
+ 'p7b' => 'application/x-pkcs7-certificates',
696
+ 'p7c' => 'application/pkcs7-mime',
697
+ 'p7m' => 'application/pkcs7-mime',
698
+ 'p7r' => 'application/x-pkcs7-certreqresp',
699
+ 'p7s' => 'application/pkcs7-signature',
700
+ 'p8' => 'application/pkcs8',
701
+ 'p10' => 'application/x-pkcs10',
702
+ 'p12' => 'application/x-pkcs12',
703
+ 'pac' => 'application/x-ns-proxy-autoconfig',
704
+ 'pages' => 'application/x-iwork-pages-sffpages',
705
+ 'pas' => 'text/x-pascal',
706
+ 'paw' => 'application/vnd.pawaafile',
707
+ 'pbd' => 'application/vnd.powerbuilder6',
708
+ 'pbm' => 'image/x-portable-bitmap',
709
+ 'pcap' => 'application/vnd.tcpdump.pcap',
710
+ 'pcf' => 'application/x-font-pcf',
711
+ 'pcl' => 'application/vnd.hp-pcl',
712
+ 'pclxl' => 'application/vnd.hp-pclxl',
713
+ 'pct' => 'image/x-pict',
714
+ 'pcurl' => 'application/vnd.curl.pcurl',
715
+ 'pcx' => 'image/x-pcx',
716
+ 'pdb' => 'application/x-pilot',
717
+ 'pde' => 'text/x-processing',
718
+ 'pdf' => 'application/pdf',
719
+ 'pem' => 'application/x-x509-user-cert',
720
+ 'pfa' => 'application/x-font-type1',
721
+ 'pfb' => 'application/x-font-type1',
722
+ 'pfm' => 'application/x-font-type1',
723
+ 'pfr' => 'application/font-tdpfr',
724
+ 'pfx' => 'application/x-pkcs12',
725
+ 'pgm' => 'image/x-portable-graymap',
726
+ 'pgn' => 'application/x-chess-pgn',
727
+ 'pgp' => 'application/pgp',
728
+ 'phar' => 'application/octet-stream',
729
+ 'php' => 'application/x-httpd-php',
730
+ 'php3' => 'application/x-httpd-php',
731
+ 'php4' => 'application/x-httpd-php',
732
+ 'phps' => 'application/x-httpd-php-source',
733
+ 'phtml' => 'application/x-httpd-php',
734
+ 'pic' => 'image/x-pict',
735
+ 'pkg' => 'application/octet-stream',
736
+ 'pki' => 'application/pkixcmp',
737
+ 'pkipath' => 'application/pkix-pkipath',
738
+ 'pkpass' => 'application/vnd.apple.pkpass',
739
+ 'pl' => 'application/x-perl',
740
+ 'plb' => 'application/vnd.3gpp.pic-bw-large',
741
+ 'plc' => 'application/vnd.mobius.plc',
742
+ 'plf' => 'application/vnd.pocketlearn',
743
+ 'pls' => 'application/pls+xml',
744
+ 'pm' => 'application/x-perl',
745
+ 'pml' => 'application/vnd.ctc-posml',
746
+ 'png' => 'image/png',
747
+ 'pnm' => 'image/x-portable-anymap',
748
+ 'portpkg' => 'application/vnd.macports.portpkg',
749
+ 'pot' => 'application/vnd.ms-powerpoint',
750
+ 'potm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
751
+ 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
752
+ 'ppa' => 'application/vnd.ms-powerpoint',
753
+ 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
754
+ 'ppd' => 'application/vnd.cups-ppd',
755
+ 'ppm' => 'image/x-portable-pixmap',
756
+ 'pps' => 'application/vnd.ms-powerpoint',
757
+ 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
758
+ 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
759
+ 'ppt' => 'application/powerpoint',
760
+ 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
761
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
762
+ 'pqa' => 'application/vnd.palm',
763
+ 'prc' => 'model/prc',
764
+ 'pre' => 'application/vnd.lotus-freelance',
765
+ 'prf' => 'application/pics-rules',
766
+ 'provx' => 'application/provenance+xml',
767
+ 'ps' => 'application/postscript',
768
+ 'psb' => 'application/vnd.3gpp.pic-bw-small',
769
+ 'psd' => 'application/x-photoshop',
770
+ 'psf' => 'application/x-font-linux-psf',
771
+ 'pskcxml' => 'application/pskc+xml',
772
+ 'pti' => 'image/prs.pti',
773
+ 'ptid' => 'application/vnd.pvi.ptid1',
774
+ 'pub' => 'application/x-mspublisher',
775
+ 'pvb' => 'application/vnd.3gpp.pic-bw-var',
776
+ 'pwn' => 'application/vnd.3m.post-it-notes',
777
+ 'pya' => 'audio/vnd.ms-playready.media.pya',
778
+ 'pyv' => 'video/vnd.ms-playready.media.pyv',
779
+ 'qam' => 'application/vnd.epson.quickanime',
780
+ 'qbo' => 'application/vnd.intu.qbo',
781
+ 'qfx' => 'application/vnd.intu.qfx',
782
+ 'qps' => 'application/vnd.publishare-delta-tree',
783
+ 'qt' => 'video/quicktime',
784
+ 'qwd' => 'application/vnd.quark.quarkxpress',
785
+ 'qwt' => 'application/vnd.quark.quarkxpress',
786
+ 'qxb' => 'application/vnd.quark.quarkxpress',
787
+ 'qxd' => 'application/vnd.quark.quarkxpress',
788
+ 'qxl' => 'application/vnd.quark.quarkxpress',
789
+ 'qxt' => 'application/vnd.quark.quarkxpress',
790
+ 'ra' => 'audio/x-realaudio',
791
+ 'ram' => 'audio/x-pn-realaudio',
792
+ 'raml' => 'application/raml+yaml',
793
+ 'rapd' => 'application/route-apd+xml',
794
+ 'rar' => 'application/x-rar',
795
+ 'ras' => 'image/x-cmu-raster',
796
+ 'rcprofile' => 'application/vnd.ipunplugged.rcprofile',
797
+ 'rdf' => 'application/rdf+xml',
798
+ 'rdz' => 'application/vnd.data-vision.rdz',
799
+ 'relo' => 'application/p2p-overlay+xml',
800
+ 'rep' => 'application/vnd.businessobjects',
801
+ 'res' => 'application/x-dtbresource+xml',
802
+ 'rgb' => 'image/x-rgb',
803
+ 'rif' => 'application/reginfo+xml',
804
+ 'rip' => 'audio/vnd.rip',
805
+ 'ris' => 'application/x-research-info-systems',
806
+ 'rl' => 'application/resource-lists+xml',
807
+ 'rlc' => 'image/vnd.fujixerox.edmics-rlc',
808
+ 'rld' => 'application/resource-lists-diff+xml',
809
+ 'rm' => 'audio/x-pn-realaudio',
810
+ 'rmi' => 'audio/midi',
811
+ 'rmp' => 'audio/x-pn-realaudio-plugin',
812
+ 'rms' => 'application/vnd.jcp.javame.midlet-rms',
813
+ 'rmvb' => 'application/vnd.rn-realmedia-vbr',
814
+ 'rnc' => 'application/relax-ng-compact-syntax',
815
+ 'rng' => 'application/xml',
816
+ 'roa' => 'application/rpki-roa',
817
+ 'roff' => 'text/troff',
818
+ 'rp9' => 'application/vnd.cloanto.rp9',
819
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
820
+ 'rpss' => 'application/vnd.nokia.radio-presets',
821
+ 'rpst' => 'application/vnd.nokia.radio-preset',
822
+ 'rq' => 'application/sparql-query',
823
+ 'rs' => 'application/rls-services+xml',
824
+ 'rsa' => 'application/x-pkcs7',
825
+ 'rsat' => 'application/atsc-rsat+xml',
826
+ 'rsd' => 'application/rsd+xml',
827
+ 'rsheet' => 'application/urc-ressheet+xml',
828
+ 'rss' => 'application/rss+xml',
829
+ 'rtf' => 'text/rtf',
830
+ 'rtx' => 'text/richtext',
831
+ 'run' => 'application/x-makeself',
832
+ 'rusd' => 'application/route-usd+xml',
833
+ 'rv' => 'video/vnd.rn-realvideo',
834
+ 's' => 'text/x-asm',
835
+ 's3m' => 'audio/s3m',
836
+ 'saf' => 'application/vnd.yamaha.smaf-audio',
837
+ 'sass' => 'text/x-sass',
838
+ 'sbml' => 'application/sbml+xml',
839
+ 'sc' => 'application/vnd.ibm.secure-container',
840
+ 'scd' => 'application/x-msschedule',
841
+ 'scm' => 'application/vnd.lotus-screencam',
842
+ 'scq' => 'application/scvp-cv-request',
843
+ 'scs' => 'application/scvp-cv-response',
844
+ 'scss' => 'text/x-scss',
845
+ 'scurl' => 'text/vnd.curl.scurl',
846
+ 'sda' => 'application/vnd.stardivision.draw',
847
+ 'sdc' => 'application/vnd.stardivision.calc',
848
+ 'sdd' => 'application/vnd.stardivision.impress',
849
+ 'sdkd' => 'application/vnd.solent.sdkm+xml',
850
+ 'sdkm' => 'application/vnd.solent.sdkm+xml',
851
+ 'sdp' => 'application/sdp',
852
+ 'sdw' => 'application/vnd.stardivision.writer',
853
+ 'sea' => 'application/octet-stream',
854
+ 'see' => 'application/vnd.seemail',
855
+ 'seed' => 'application/vnd.fdsn.seed',
856
+ 'sema' => 'application/vnd.sema',
857
+ 'semd' => 'application/vnd.semd',
858
+ 'semf' => 'application/vnd.semf',
859
+ 'senmlx' => 'application/senml+xml',
860
+ 'sensmlx' => 'application/sensml+xml',
861
+ 'ser' => 'application/java-serialized-object',
862
+ 'setpay' => 'application/set-payment-initiation',
863
+ 'setreg' => 'application/set-registration-initiation',
864
+ 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data',
865
+ 'sfs' => 'application/vnd.spotfire.sfs',
866
+ 'sfv' => 'text/x-sfv',
867
+ 'sgi' => 'image/sgi',
868
+ 'sgl' => 'application/vnd.stardivision.writer-global',
869
+ 'sgm' => 'text/sgml',
870
+ 'sgml' => 'text/sgml',
871
+ 'sh' => 'application/x-sh',
872
+ 'shar' => 'application/x-shar',
873
+ 'shex' => 'text/shex',
874
+ 'shf' => 'application/shf+xml',
875
+ 'shtml' => 'text/html',
876
+ 'sid' => 'image/x-mrsid-image',
877
+ 'sieve' => 'application/sieve',
878
+ 'sig' => 'application/pgp-signature',
879
+ 'sil' => 'audio/silk',
880
+ 'silo' => 'model/mesh',
881
+ 'sis' => 'application/vnd.symbian.install',
882
+ 'sisx' => 'application/vnd.symbian.install',
883
+ 'sit' => 'application/x-stuffit',
884
+ 'sitx' => 'application/x-stuffitx',
885
+ 'siv' => 'application/sieve',
886
+ 'skd' => 'application/vnd.koan',
887
+ 'skm' => 'application/vnd.koan',
888
+ 'skp' => 'application/vnd.koan',
889
+ 'skt' => 'application/vnd.koan',
890
+ 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12',
891
+ 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
892
+ 'slim' => 'text/slim',
893
+ 'slm' => 'text/slim',
894
+ 'sls' => 'application/route-s-tsid+xml',
895
+ 'slt' => 'application/vnd.epson.salt',
896
+ 'sm' => 'application/vnd.stepmania.stepchart',
897
+ 'smf' => 'application/vnd.stardivision.math',
898
+ 'smi' => 'application/smil',
899
+ 'smil' => 'application/smil',
900
+ 'smv' => 'video/x-smv',
901
+ 'smzip' => 'application/vnd.stepmania.package',
902
+ 'snd' => 'audio/basic',
903
+ 'snf' => 'application/x-font-snf',
904
+ 'so' => 'application/octet-stream',
905
+ 'spc' => 'application/x-pkcs7-certificates',
906
+ 'spdx' => 'text/spdx',
907
+ 'spf' => 'application/vnd.yamaha.smaf-phrase',
908
+ 'spl' => 'application/x-futuresplash',
909
+ 'spot' => 'text/vnd.in3d.spot',
910
+ 'spp' => 'application/scvp-vp-response',
911
+ 'spq' => 'application/scvp-vp-request',
912
+ 'spx' => 'audio/ogg',
913
+ 'sql' => 'application/x-sql',
914
+ 'src' => 'application/x-wais-source',
915
+ 'srt' => 'application/x-subrip',
916
+ 'sru' => 'application/sru+xml',
917
+ 'srx' => 'application/sparql-results+xml',
918
+ 'ssdl' => 'application/ssdl+xml',
919
+ 'sse' => 'application/vnd.kodak-descriptor',
920
+ 'ssf' => 'application/vnd.epson.ssf',
921
+ 'ssml' => 'application/ssml+xml',
922
+ 'sst' => 'application/octet-stream',
923
+ 'st' => 'application/vnd.sailingtracker.track',
924
+ 'stc' => 'application/vnd.sun.xml.calc.template',
925
+ 'std' => 'application/vnd.sun.xml.draw.template',
926
+ 'stf' => 'application/vnd.wt.stf',
927
+ 'sti' => 'application/vnd.sun.xml.impress.template',
928
+ 'stk' => 'application/hyperstudio',
929
+ 'stl' => 'model/stl',
930
+ 'stpx' => 'model/step+xml',
931
+ 'stpxz' => 'model/step-xml+zip',
932
+ 'stpz' => 'model/step+zip',
933
+ 'str' => 'application/vnd.pg.format',
934
+ 'stw' => 'application/vnd.sun.xml.writer.template',
935
+ 'styl' => 'text/stylus',
936
+ 'stylus' => 'text/stylus',
937
+ 'sub' => 'text/vnd.dvb.subtitle',
938
+ 'sus' => 'application/vnd.sus-calendar',
939
+ 'susp' => 'application/vnd.sus-calendar',
940
+ 'sv4cpio' => 'application/x-sv4cpio',
941
+ 'sv4crc' => 'application/x-sv4crc',
942
+ 'svc' => 'application/vnd.dvb.service',
943
+ 'svd' => 'application/vnd.svd',
944
+ 'svg' => 'image/svg+xml',
945
+ 'svgz' => 'image/svg+xml',
946
+ 'swa' => 'application/x-director',
947
+ 'swf' => 'application/x-shockwave-flash',
948
+ 'swi' => 'application/vnd.aristanetworks.swi',
949
+ 'swidtag' => 'application/swid+xml',
950
+ 'sxc' => 'application/vnd.sun.xml.calc',
951
+ 'sxd' => 'application/vnd.sun.xml.draw',
952
+ 'sxg' => 'application/vnd.sun.xml.writer.global',
953
+ 'sxi' => 'application/vnd.sun.xml.impress',
954
+ 'sxm' => 'application/vnd.sun.xml.math',
955
+ 'sxw' => 'application/vnd.sun.xml.writer',
956
+ 't' => 'text/troff',
957
+ 't3' => 'application/x-t3vm-image',
958
+ 't38' => 'image/t38',
959
+ 'taglet' => 'application/vnd.mynfc',
960
+ 'tao' => 'application/vnd.tao.intent-module-archive',
961
+ 'tap' => 'image/vnd.tencent.tap',
962
+ 'tar' => 'application/x-tar',
963
+ 'tcap' => 'application/vnd.3gpp2.tcap',
964
+ 'tcl' => 'application/x-tcl',
965
+ 'td' => 'application/urc-targetdesc+xml',
966
+ 'teacher' => 'application/vnd.smart.teacher',
967
+ 'tei' => 'application/tei+xml',
968
+ 'teicorpus' => 'application/tei+xml',
969
+ 'tex' => 'application/x-tex',
970
+ 'texi' => 'application/x-texinfo',
971
+ 'texinfo' => 'application/x-texinfo',
972
+ 'text' => 'text/plain',
973
+ 'tfi' => 'application/thraud+xml',
974
+ 'tfm' => 'application/x-tex-tfm',
975
+ 'tfx' => 'image/tiff-fx',
976
+ 'tga' => 'image/x-tga',
977
+ 'tgz' => 'application/x-tar',
978
+ 'thmx' => 'application/vnd.ms-officetheme',
979
+ 'tif' => 'image/tiff',
980
+ 'tiff' => 'image/tiff',
981
+ 'tk' => 'application/x-tcl',
982
+ 'tmo' => 'application/vnd.tmobile-livetv',
983
+ 'toml' => 'application/toml',
984
+ 'torrent' => 'application/x-bittorrent',
985
+ 'tpl' => 'application/vnd.groove-tool-template',
986
+ 'tpt' => 'application/vnd.trid.tpt',
987
+ 'tr' => 'text/troff',
988
+ 'tra' => 'application/vnd.trueapp',
989
+ 'trig' => 'application/trig',
990
+ 'trm' => 'application/x-msterminal',
991
+ 'ts' => 'video/mp2t',
992
+ 'tsd' => 'application/timestamped-data',
993
+ 'tsv' => 'text/tab-separated-values',
994
+ 'ttc' => 'font/collection',
995
+ 'ttf' => 'font/ttf',
996
+ 'ttl' => 'text/turtle',
997
+ 'ttml' => 'application/ttml+xml',
998
+ 'twd' => 'application/vnd.simtech-mindmapper',
999
+ 'twds' => 'application/vnd.simtech-mindmapper',
1000
+ 'txd' => 'application/vnd.genomatix.tuxedo',
1001
+ 'txf' => 'application/vnd.mobius.txf',
1002
+ 'txt' => 'text/plain',
1003
+ 'u3d' => 'model/u3d',
1004
+ 'u8dsn' => 'message/global-delivery-status',
1005
+ 'u8hdr' => 'message/global-headers',
1006
+ 'u8mdn' => 'message/global-disposition-notification',
1007
+ 'u8msg' => 'message/global',
1008
+ 'u32' => 'application/x-authorware-bin',
1009
+ 'ubj' => 'application/ubjson',
1010
+ 'udeb' => 'application/x-debian-package',
1011
+ 'ufd' => 'application/vnd.ufdl',
1012
+ 'ufdl' => 'application/vnd.ufdl',
1013
+ 'ulx' => 'application/x-glulx',
1014
+ 'umj' => 'application/vnd.umajin',
1015
+ 'unityweb' => 'application/vnd.unity',
1016
+ 'uoml' => 'application/vnd.uoml+xml',
1017
+ 'uri' => 'text/uri-list',
1018
+ 'uris' => 'text/uri-list',
1019
+ 'urls' => 'text/uri-list',
1020
+ 'usdz' => 'model/vnd.usdz+zip',
1021
+ 'ustar' => 'application/x-ustar',
1022
+ 'utz' => 'application/vnd.uiq.theme',
1023
+ 'uu' => 'text/x-uuencode',
1024
+ 'uva' => 'audio/vnd.dece.audio',
1025
+ 'uvd' => 'application/vnd.dece.data',
1026
+ 'uvf' => 'application/vnd.dece.data',
1027
+ 'uvg' => 'image/vnd.dece.graphic',
1028
+ 'uvh' => 'video/vnd.dece.hd',
1029
+ 'uvi' => 'image/vnd.dece.graphic',
1030
+ 'uvm' => 'video/vnd.dece.mobile',
1031
+ 'uvp' => 'video/vnd.dece.pd',
1032
+ 'uvs' => 'video/vnd.dece.sd',
1033
+ 'uvt' => 'application/vnd.dece.ttml+xml',
1034
+ 'uvu' => 'video/vnd.uvvu.mp4',
1035
+ 'uvv' => 'video/vnd.dece.video',
1036
+ 'uvva' => 'audio/vnd.dece.audio',
1037
+ 'uvvd' => 'application/vnd.dece.data',
1038
+ 'uvvf' => 'application/vnd.dece.data',
1039
+ 'uvvg' => 'image/vnd.dece.graphic',
1040
+ 'uvvh' => 'video/vnd.dece.hd',
1041
+ 'uvvi' => 'image/vnd.dece.graphic',
1042
+ 'uvvm' => 'video/vnd.dece.mobile',
1043
+ 'uvvp' => 'video/vnd.dece.pd',
1044
+ 'uvvs' => 'video/vnd.dece.sd',
1045
+ 'uvvt' => 'application/vnd.dece.ttml+xml',
1046
+ 'uvvu' => 'video/vnd.uvvu.mp4',
1047
+ 'uvvv' => 'video/vnd.dece.video',
1048
+ 'uvvx' => 'application/vnd.dece.unspecified',
1049
+ 'uvvz' => 'application/vnd.dece.zip',
1050
+ 'uvx' => 'application/vnd.dece.unspecified',
1051
+ 'uvz' => 'application/vnd.dece.zip',
1052
+ 'vbox' => 'application/x-virtualbox-vbox',
1053
+ 'vbox-extpack' => 'application/x-virtualbox-vbox-extpack',
1054
+ 'vcard' => 'text/vcard',
1055
+ 'vcd' => 'application/x-cdlink',
1056
+ 'vcf' => 'text/x-vcard',
1057
+ 'vcg' => 'application/vnd.groove-vcard',
1058
+ 'vcs' => 'text/x-vcalendar',
1059
+ 'vcx' => 'application/vnd.vcx',
1060
+ 'vdi' => 'application/x-virtualbox-vdi',
1061
+ 'vds' => 'model/vnd.sap.vds',
1062
+ 'vhd' => 'application/x-virtualbox-vhd',
1063
+ 'vis' => 'application/vnd.visionary',
1064
+ 'viv' => 'video/vnd.vivo',
1065
+ 'vlc' => 'application/videolan',
1066
+ 'vmdk' => 'application/x-virtualbox-vmdk',
1067
+ 'vob' => 'video/x-ms-vob',
1068
+ 'vor' => 'application/vnd.stardivision.writer',
1069
+ 'vox' => 'application/x-authorware-bin',
1070
+ 'vrml' => 'model/vrml',
1071
+ 'vsd' => 'application/vnd.visio',
1072
+ 'vsf' => 'application/vnd.vsf',
1073
+ 'vss' => 'application/vnd.visio',
1074
+ 'vst' => 'application/vnd.visio',
1075
+ 'vsw' => 'application/vnd.visio',
1076
+ 'vtf' => 'image/vnd.valve.source.texture',
1077
+ 'vtt' => 'text/vtt',
1078
+ 'vtu' => 'model/vnd.vtu',
1079
+ 'vxml' => 'application/voicexml+xml',
1080
+ 'w3d' => 'application/x-director',
1081
+ 'wad' => 'application/x-doom',
1082
+ 'wadl' => 'application/vnd.sun.wadl+xml',
1083
+ 'war' => 'application/java-archive',
1084
+ 'wasm' => 'application/wasm',
1085
+ 'wav' => 'audio/x-wav',
1086
+ 'wax' => 'audio/x-ms-wax',
1087
+ 'wbmp' => 'image/vnd.wap.wbmp',
1088
+ 'wbs' => 'application/vnd.criticaltools.wbs+xml',
1089
+ 'wbxml' => 'application/wbxml',
1090
+ 'wcm' => 'application/vnd.ms-works',
1091
+ 'wdb' => 'application/vnd.ms-works',
1092
+ 'wdp' => 'image/vnd.ms-photo',
1093
+ 'weba' => 'audio/webm',
1094
+ 'webapp' => 'application/x-web-app-manifest+json',
1095
+ 'webm' => 'video/webm',
1096
+ 'webmanifest' => 'application/manifest+json',
1097
+ 'webp' => 'image/webp',
1098
+ 'wg' => 'application/vnd.pmi.widget',
1099
+ 'wgt' => 'application/widget',
1100
+ 'wif' => 'application/watcherinfo+xml',
1101
+ 'wks' => 'application/vnd.ms-works',
1102
+ 'wm' => 'video/x-ms-wm',
1103
+ 'wma' => 'audio/x-ms-wma',
1104
+ 'wmd' => 'application/x-ms-wmd',
1105
+ 'wmf' => 'image/wmf',
1106
+ 'wml' => 'text/vnd.wap.wml',
1107
+ 'wmlc' => 'application/wmlc',
1108
+ 'wmls' => 'text/vnd.wap.wmlscript',
1109
+ 'wmlsc' => 'application/vnd.wap.wmlscriptc',
1110
+ 'wmv' => 'video/x-ms-wmv',
1111
+ 'wmx' => 'video/x-ms-wmx',
1112
+ 'wmz' => 'application/x-msmetafile',
1113
+ 'woff' => 'font/woff',
1114
+ 'woff2' => 'font/woff2',
1115
+ 'word' => 'application/msword',
1116
+ 'wpd' => 'application/vnd.wordperfect',
1117
+ 'wpl' => 'application/vnd.ms-wpl',
1118
+ 'wps' => 'application/vnd.ms-works',
1119
+ 'wqd' => 'application/vnd.wqd',
1120
+ 'wri' => 'application/x-mswrite',
1121
+ 'wrl' => 'model/vrml',
1122
+ 'wsc' => 'message/vnd.wfa.wsc',
1123
+ 'wsdl' => 'application/wsdl+xml',
1124
+ 'wspolicy' => 'application/wspolicy+xml',
1125
+ 'wtb' => 'application/vnd.webturbo',
1126
+ 'wvx' => 'video/x-ms-wvx',
1127
+ 'x3d' => 'model/x3d+xml',
1128
+ 'x3db' => 'model/x3d+fastinfoset',
1129
+ 'x3dbz' => 'model/x3d+binary',
1130
+ 'x3dv' => 'model/x3d-vrml',
1131
+ 'x3dvz' => 'model/x3d+vrml',
1132
+ 'x3dz' => 'model/x3d+xml',
1133
+ 'x32' => 'application/x-authorware-bin',
1134
+ 'x_b' => 'model/vnd.parasolid.transmit.binary',
1135
+ 'x_t' => 'model/vnd.parasolid.transmit.text',
1136
+ 'xaml' => 'application/xaml+xml',
1137
+ 'xap' => 'application/x-silverlight-app',
1138
+ 'xar' => 'application/vnd.xara',
1139
+ 'xav' => 'application/xcap-att+xml',
1140
+ 'xbap' => 'application/x-ms-xbap',
1141
+ 'xbd' => 'application/vnd.fujixerox.docuworks.binder',
1142
+ 'xbm' => 'image/x-xbitmap',
1143
+ 'xca' => 'application/xcap-caps+xml',
1144
+ 'xcs' => 'application/calendar+xml',
1145
+ 'xdf' => 'application/xcap-diff+xml',
1146
+ 'xdm' => 'application/vnd.syncml.dm+xml',
1147
+ 'xdp' => 'application/vnd.adobe.xdp+xml',
1148
+ 'xdssc' => 'application/dssc+xml',
1149
+ 'xdw' => 'application/vnd.fujixerox.docuworks',
1150
+ 'xel' => 'application/xcap-el+xml',
1151
+ 'xenc' => 'application/xenc+xml',
1152
+ 'xer' => 'application/patch-ops-error+xml',
1153
+ 'xfdf' => 'application/vnd.adobe.xfdf',
1154
+ 'xfdl' => 'application/vnd.xfdl',
1155
+ 'xht' => 'application/xhtml+xml',
1156
+ 'xhtml' => 'application/xhtml+xml',
1157
+ 'xhvml' => 'application/xv+xml',
1158
+ 'xif' => 'image/vnd.xiff',
1159
+ 'xl' => 'application/excel',
1160
+ 'xla' => 'application/vnd.ms-excel',
1161
+ 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
1162
+ 'xlc' => 'application/vnd.ms-excel',
1163
+ 'xlf' => 'application/xliff+xml',
1164
+ 'xlm' => 'application/vnd.ms-excel',
1165
+ 'xls' => 'application/vnd.ms-excel',
1166
+ 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
1167
+ 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
1168
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
1169
+ 'xlt' => 'application/vnd.ms-excel',
1170
+ 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
1171
+ 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
1172
+ 'xlw' => 'application/vnd.ms-excel',
1173
+ 'xm' => 'audio/xm',
1174
+ 'xml' => 'application/xml',
1175
+ 'xns' => 'application/xcap-ns+xml',
1176
+ 'xo' => 'application/vnd.olpc-sugar',
1177
+ 'xop' => 'application/xop+xml',
1178
+ 'xpi' => 'application/x-xpinstall',
1179
+ 'xpl' => 'application/xproc+xml',
1180
+ 'xpm' => 'image/x-xpixmap',
1181
+ 'xpr' => 'application/vnd.is-xpr',
1182
+ 'xps' => 'application/vnd.ms-xpsdocument',
1183
+ 'xpw' => 'application/vnd.intercon.formnet',
1184
+ 'xpx' => 'application/vnd.intercon.formnet',
1185
+ 'xsd' => 'application/xml',
1186
+ 'xsl' => 'application/xml',
1187
+ 'xslt' => 'application/xslt+xml',
1188
+ 'xsm' => 'application/vnd.syncml+xml',
1189
+ 'xspf' => 'application/xspf+xml',
1190
+ 'xul' => 'application/vnd.mozilla.xul+xml',
1191
+ 'xvm' => 'application/xv+xml',
1192
+ 'xvml' => 'application/xv+xml',
1193
+ 'xwd' => 'image/x-xwindowdump',
1194
+ 'xyz' => 'chemical/x-xyz',
1195
+ 'xz' => 'application/x-xz',
1196
+ 'yaml' => 'text/yaml',
1197
+ 'yang' => 'application/yang',
1198
+ 'yin' => 'application/yin+xml',
1199
+ 'yml' => 'text/yaml',
1200
+ 'ymp' => 'text/x-suse-ymp',
1201
+ 'z' => 'application/x-compress',
1202
+ 'z1' => 'application/x-zmachine',
1203
+ 'z2' => 'application/x-zmachine',
1204
+ 'z3' => 'application/x-zmachine',
1205
+ 'z4' => 'application/x-zmachine',
1206
+ 'z5' => 'application/x-zmachine',
1207
+ 'z6' => 'application/x-zmachine',
1208
+ 'z7' => 'application/x-zmachine',
1209
+ 'z8' => 'application/x-zmachine',
1210
+ 'zaz' => 'application/vnd.zzazz.deck+xml',
1211
+ 'zip' => 'application/zip',
1212
+ 'zir' => 'application/vnd.zul',
1213
+ 'zirz' => 'application/vnd.zul',
1214
+ 'zmm' => 'application/vnd.handheld-entertainment+xml',
1215
+ 'zsh' => 'text/x-scriptzsh',
1216
+ ];
1217
+
1218
+ /**
1219
+ * Determines the mimetype of a file by looking at its extension.
1220
+ *
1221
+ * @link https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
1222
+ */
1223
+ public static function fromFilename(string $filename): ?string
1224
+ {
1225
+ return self::fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
1226
+ }
1227
+
1228
+ /**
1229
+ * Maps a file extensions to a mimetype.
1230
+ *
1231
+ * @link https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
1232
+ */
1233
+ public static function fromExtension(string $extension): ?string
1234
+ {
1235
+ return self::MIME_TYPES[strtolower($extension)] ?? null;
1236
+ }
1237
+ }
src/vendor/guzzlehttp/psr7/src/MultipartStream.php ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Stream that when read returns bytes for a streaming multipart or
11
+ * multipart/form-data stream.
12
+ */
13
+ final class MultipartStream implements StreamInterface
14
+ {
15
+ use StreamDecoratorTrait;
16
+
17
+ /** @var string */
18
+ private $boundary;
19
+
20
+ /**
21
+ * @param array $elements Array of associative arrays, each containing a
22
+ * required "name" key mapping to the form field,
23
+ * name, a required "contents" key mapping to a
24
+ * StreamInterface/resource/string, an optional
25
+ * "headers" associative array of custom headers,
26
+ * and an optional "filename" key mapping to a
27
+ * string to send as the filename in the part.
28
+ * @param string $boundary You can optionally provide a specific boundary
29
+ *
30
+ * @throws \InvalidArgumentException
31
+ */
32
+ public function __construct(array $elements = [], string $boundary = null)
33
+ {
34
+ $this->boundary = $boundary ?: sha1(uniqid('', true));
35
+ $this->stream = $this->createStream($elements);
36
+ }
37
+
38
+ public function getBoundary(): string
39
+ {
40
+ return $this->boundary;
41
+ }
42
+
43
+ public function isWritable(): bool
44
+ {
45
+ return false;
46
+ }
47
+
48
+ /**
49
+ * Get the headers needed before transferring the content of a POST file
50
+ *
51
+ * @param array<string, string> $headers
52
+ */
53
+ private function getHeaders(array $headers): string
54
+ {
55
+ $str = '';
56
+ foreach ($headers as $key => $value) {
57
+ $str .= "{$key}: {$value}\r\n";
58
+ }
59
+
60
+ return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
61
+ }
62
+
63
+ /**
64
+ * Create the aggregate stream that will be used to upload the POST data
65
+ */
66
+ protected function createStream(array $elements = []): StreamInterface
67
+ {
68
+ $stream = new AppendStream();
69
+
70
+ foreach ($elements as $element) {
71
+ if (!is_array($element)) {
72
+ throw new \UnexpectedValueException("An array is expected");
73
+ }
74
+ $this->addElement($stream, $element);
75
+ }
76
+
77
+ // Add the trailing boundary with CRLF
78
+ $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n"));
79
+
80
+ return $stream;
81
+ }
82
+
83
+ private function addElement(AppendStream $stream, array $element): void
84
+ {
85
+ foreach (['contents', 'name'] as $key) {
86
+ if (!array_key_exists($key, $element)) {
87
+ throw new \InvalidArgumentException("A '{$key}' key is required");
88
+ }
89
+ }
90
+
91
+ $element['contents'] = Utils::streamFor($element['contents']);
92
+
93
+ if (empty($element['filename'])) {
94
+ $uri = $element['contents']->getMetadata('uri');
95
+ if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') {
96
+ $element['filename'] = $uri;
97
+ }
98
+ }
99
+
100
+ [$body, $headers] = $this->createElement(
101
+ $element['name'],
102
+ $element['contents'],
103
+ $element['filename'] ?? null,
104
+ $element['headers'] ?? []
105
+ );
106
+
107
+ $stream->addStream(Utils::streamFor($this->getHeaders($headers)));
108
+ $stream->addStream($body);
109
+ $stream->addStream(Utils::streamFor("\r\n"));
110
+ }
111
+
112
+ private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array
113
+ {
114
+ // Set a default content-disposition header if one was no provided
115
+ $disposition = $this->getHeader($headers, 'content-disposition');
116
+ if (!$disposition) {
117
+ $headers['Content-Disposition'] = ($filename === '0' || $filename)
118
+ ? sprintf(
119
+ 'form-data; name="%s"; filename="%s"',
120
+ $name,
121
+ basename($filename)
122
+ )
123
+ : "form-data; name=\"{$name}\"";
124
+ }
125
+
126
+ // Set a default content-length header if one was no provided
127
+ $length = $this->getHeader($headers, 'content-length');
128
+ if (!$length) {
129
+ if ($length = $stream->getSize()) {
130
+ $headers['Content-Length'] = (string) $length;
131
+ }
132
+ }
133
+
134
+ // Set a default Content-Type if one was not supplied
135
+ $type = $this->getHeader($headers, 'content-type');
136
+ if (!$type && ($filename === '0' || $filename)) {
137
+ if ($type = MimeType::fromFilename($filename)) {
138
+ $headers['Content-Type'] = $type;
139
+ }
140
+ }
141
+
142
+ return [$stream, $headers];
143
+ }
144
+
145
+ private function getHeader(array $headers, string $key)
146
+ {
147
+ $lowercaseHeader = strtolower($key);
148
+ foreach ($headers as $k => $v) {
149
+ if (strtolower($k) === $lowercaseHeader) {
150
+ return $v;
151
+ }
152
+ }
153
+
154
+ return null;
155
+ }
156
+ }
src/vendor/guzzlehttp/psr7/src/NoSeekStream.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Stream decorator that prevents a stream from being seeked.
11
+ */
12
+ final class NoSeekStream implements StreamInterface
13
+ {
14
+ use StreamDecoratorTrait;
15
+
16
+ public function seek($offset, $whence = SEEK_SET): void
17
+ {
18
+ throw new \RuntimeException('Cannot seek a NoSeekStream');
19
+ }
20
+
21
+ public function isSeekable(): bool
22
+ {
23
+ return false;
24
+ }
25
+ }
src/vendor/guzzlehttp/psr7/src/PumpStream.php ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Provides a read only stream that pumps data from a PHP callable.
11
+ *
12
+ * When invoking the provided callable, the PumpStream will pass the amount of
13
+ * data requested to read to the callable. The callable can choose to ignore
14
+ * this value and return fewer or more bytes than requested. Any extra data
15
+ * returned by the provided callable is buffered internally until drained using
16
+ * the read() function of the PumpStream. The provided callable MUST return
17
+ * false when there is no more data to read.
18
+ */
19
+ final class PumpStream implements StreamInterface
20
+ {
21
+ /** @var callable|null */
22
+ private $source;
23
+
24
+ /** @var int|null */
25
+ private $size;
26
+
27
+ /** @var int */
28
+ private $tellPos = 0;
29
+
30
+ /** @var array */
31
+ private $metadata;
32
+
33
+ /** @var BufferStream */
34
+ private $buffer;
35
+
36
+ /**
37
+ * @param callable(int): (string|null|false) $source Source of the stream data. The callable MAY
38
+ * accept an integer argument used to control the
39
+ * amount of data to return. The callable MUST
40
+ * return a string when called, or false|null on error
41
+ * or EOF.
42
+ * @param array{size?: int, metadata?: array} $options Stream options:
43
+ * - metadata: Hash of metadata to use with stream.
44
+ * - size: Size of the stream, if known.
45
+ */
46
+ public function __construct(callable $source, array $options = [])
47
+ {
48
+ $this->source = $source;
49
+ $this->size = $options['size'] ?? null;
50
+ $this->metadata = $options['metadata'] ?? [];
51
+ $this->buffer = new BufferStream();
52
+ }
53
+
54
+ public function __toString(): string
55
+ {
56
+ try {
57
+ return Utils::copyToString($this);
58
+ } catch (\Throwable $e) {
59
+ if (\PHP_VERSION_ID >= 70400) {
60
+ throw $e;
61
+ }
62
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
63
+ return '';
64
+ }
65
+ }
66
+
67
+ public function close(): void
68
+ {
69
+ $this->detach();
70
+ }
71
+
72
+ public function detach()
73
+ {
74
+ $this->tellPos = 0;
75
+ $this->source = null;
76
+
77
+ return null;
78
+ }
79
+
80
+ public function getSize(): ?int
81
+ {
82
+ return $this->size;
83
+ }
84
+
85
+ public function tell(): int
86
+ {
87
+ return $this->tellPos;
88
+ }
89
+
90
+ public function eof(): bool
91
+ {
92
+ return $this->source === null;
93
+ }
94
+
95
+ public function isSeekable(): bool
96
+ {
97
+ return false;
98
+ }
99
+
100
+ public function rewind(): void
101
+ {
102
+ $this->seek(0);
103
+ }
104
+
105
+ public function seek($offset, $whence = SEEK_SET): void
106
+ {
107
+ throw new \RuntimeException('Cannot seek a PumpStream');
108
+ }
109
+
110
+ public function isWritable(): bool
111
+ {
112
+ return false;
113
+ }
114
+
115
+ public function write($string): int
116
+ {
117
+ throw new \RuntimeException('Cannot write to a PumpStream');
118
+ }
119
+
120
+ public function isReadable(): bool
121
+ {
122
+ return true;
123
+ }
124
+
125
+ public function read($length): string
126
+ {
127
+ $data = $this->buffer->read($length);
128
+ $readLen = strlen($data);
129
+ $this->tellPos += $readLen;
130
+ $remaining = $length - $readLen;
131
+
132
+ if ($remaining) {
133
+ $this->pump($remaining);
134
+ $data .= $this->buffer->read($remaining);
135
+ $this->tellPos += strlen($data) - $readLen;
136
+ }
137
+
138
+ return $data;
139
+ }
140
+
141
+ public function getContents(): string
142
+ {
143
+ $result = '';
144
+ while (!$this->eof()) {
145
+ $result .= $this->read(1000000);
146
+ }
147
+
148
+ return $result;
149
+ }
150
+
151
+ /**
152
+ * {@inheritdoc}
153
+ *
154
+ * @return mixed
155
+ */
156
+ public function getMetadata($key = null)
157
+ {
158
+ if (!$key) {
159
+ return $this->metadata;
160
+ }
161
+
162
+ return $this->metadata[$key] ?? null;
163
+ }
164
+
165
+ private function pump(int $length): void
166
+ {
167
+ if ($this->source) {
168
+ do {
169
+ $data = call_user_func($this->source, $length);
170
+ if ($data === false || $data === null) {
171
+ $this->source = null;
172
+ return;
173
+ }
174
+ $this->buffer->write($data);
175
+ $length -= strlen($data);
176
+ } while ($length > 0);
177
+ }
178
+ }
179
+ }
src/vendor/guzzlehttp/psr7/src/Query.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ final class Query
8
+ {
9
+ /**
10
+ * Parse a query string into an associative array.
11
+ *
12
+ * If multiple values are found for the same key, the value of that key
13
+ * value pair will become an array. This function does not parse nested
14
+ * PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
15
+ * will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
16
+ *
17
+ * @param string $str Query string to parse
18
+ * @param int|bool $urlEncoding How the query string is encoded
19
+ */
20
+ public static function parse(string $str, $urlEncoding = true): array
21
+ {
22
+ $result = [];
23
+
24
+ if ($str === '') {
25
+ return $result;
26
+ }
27
+
28
+ if ($urlEncoding === true) {
29
+ $decoder = function ($value) {
30
+ return rawurldecode(str_replace('+', ' ', (string) $value));
31
+ };
32
+ } elseif ($urlEncoding === PHP_QUERY_RFC3986) {
33
+ $decoder = 'rawurldecode';
34
+ } elseif ($urlEncoding === PHP_QUERY_RFC1738) {
35
+ $decoder = 'urldecode';
36
+ } else {
37
+ $decoder = function ($str) {
38
+ return $str;
39
+ };
40
+ }
41
+
42
+ foreach (explode('&', $str) as $kvp) {
43
+ $parts = explode('=', $kvp, 2);
44
+ $key = $decoder($parts[0]);
45
+ $value = isset($parts[1]) ? $decoder($parts[1]) : null;
46
+ if (!array_key_exists($key, $result)) {
47
+ $result[$key] = $value;
48
+ } else {
49
+ if (!is_array($result[$key])) {
50
+ $result[$key] = [$result[$key]];
51
+ }
52
+ $result[$key][] = $value;
53
+ }
54
+ }
55
+
56
+ return $result;
57
+ }
58
+
59
+ /**
60
+ * Build a query string from an array of key value pairs.
61
+ *
62
+ * This function can use the return value of `parse()` to build a query
63
+ * string. This function does not modify the provided keys when an array is
64
+ * encountered (like `http_build_query()` would).
65
+ *
66
+ * @param array $params Query string parameters.
67
+ * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
68
+ * to encode using RFC3986, or PHP_QUERY_RFC1738
69
+ * to encode using RFC1738.
70
+ */
71
+ public static function build(array $params, $encoding = PHP_QUERY_RFC3986): string
72
+ {
73
+ if (!$params) {
74
+ return '';
75
+ }
76
+
77
+ if ($encoding === false) {
78
+ $encoder = function (string $str): string {
79
+ return $str;
80
+ };
81
+ } elseif ($encoding === PHP_QUERY_RFC3986) {
82
+ $encoder = 'rawurlencode';
83
+ } elseif ($encoding === PHP_QUERY_RFC1738) {
84
+ $encoder = 'urlencode';
85
+ } else {
86
+ throw new \InvalidArgumentException('Invalid type');
87
+ }
88
+
89
+ $qs = '';
90
+ foreach ($params as $k => $v) {
91
+ $k = $encoder((string) $k);
92
+ if (!is_array($v)) {
93
+ $qs .= $k;
94
+ $v = is_bool($v) ? (int) $v : $v;
95
+ if ($v !== null) {
96
+ $qs .= '=' . $encoder((string) $v);
97
+ }
98
+ $qs .= '&';
99
+ } else {
100
+ foreach ($v as $vv) {
101
+ $qs .= $k;
102
+ $vv = is_bool($vv) ? (int) $vv : $vv;
103
+ if ($vv !== null) {
104
+ $qs .= '=' . $encoder((string) $vv);
105
+ }
106
+ $qs .= '&';
107
+ }
108
+ }
109
+ }
110
+
111
+ return $qs ? (string) substr($qs, 0, -1) : '';
112
+ }
113
+ }
src/vendor/guzzlehttp/psr7/src/Request.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use InvalidArgumentException;
8
+ use Psr\Http\Message\RequestInterface;
9
+ use Psr\Http\Message\StreamInterface;
10
+ use Psr\Http\Message\UriInterface;
11
+
12
+ /**
13
+ * PSR-7 request implementation.
14
+ */
15
+ class Request implements RequestInterface
16
+ {
17
+ use MessageTrait;
18
+
19
+ /** @var string */
20
+ private $method;
21
+
22
+ /** @var string|null */
23
+ private $requestTarget;
24
+
25
+ /** @var UriInterface */
26
+ private $uri;
27
+
28
+ /**
29
+ * @param string $method HTTP method
30
+ * @param string|UriInterface $uri URI
31
+ * @param array<string, string|string[]> $headers Request headers
32
+ * @param string|resource|StreamInterface|null $body Request body
33
+ * @param string $version Protocol version
34
+ */
35
+ public function __construct(
36
+ string $method,
37
+ $uri,
38
+ array $headers = [],
39
+ $body = null,
40
+ string $version = '1.1'
41
+ ) {
42
+ $this->assertMethod($method);
43
+ if (!($uri instanceof UriInterface)) {
44
+ $uri = new Uri($uri);
45
+ }
46
+
47
+ $this->method = strtoupper($method);
48
+ $this->uri = $uri;
49
+ $this->setHeaders($headers);
50
+ $this->protocol = $version;
51
+
52
+ if (!isset($this->headerNames['host'])) {
53
+ $this->updateHostFromUri();
54
+ }
55
+
56
+ if ($body !== '' && $body !== null) {
57
+ $this->stream = Utils::streamFor($body);
58
+ }
59
+ }
60
+
61
+ public function getRequestTarget(): string
62
+ {
63
+ if ($this->requestTarget !== null) {
64
+ return $this->requestTarget;
65
+ }
66
+
67
+ $target = $this->uri->getPath();
68
+ if ($target === '') {
69
+ $target = '/';
70
+ }
71
+ if ($this->uri->getQuery() != '') {
72
+ $target .= '?' . $this->uri->getQuery();
73
+ }
74
+
75
+ return $target;
76
+ }
77
+
78
+ public function withRequestTarget($requestTarget): RequestInterface
79
+ {
80
+ if (preg_match('#\s#', $requestTarget)) {
81
+ throw new InvalidArgumentException(
82
+ 'Invalid request target provided; cannot contain whitespace'
83
+ );
84
+ }
85
+
86
+ $new = clone $this;
87
+ $new->requestTarget = $requestTarget;
88
+ return $new;
89
+ }
90
+
91
+ public function getMethod(): string
92
+ {
93
+ return $this->method;
94
+ }
95
+
96
+ public function withMethod($method): RequestInterface
97
+ {
98
+ $this->assertMethod($method);
99
+ $new = clone $this;
100
+ $new->method = strtoupper($method);
101
+ return $new;
102
+ }
103
+
104
+ public function getUri(): UriInterface
105
+ {
106
+ return $this->uri;
107
+ }
108
+
109
+ public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface
110
+ {
111
+ if ($uri === $this->uri) {
112
+ return $this;
113
+ }
114
+
115
+ $new = clone $this;
116
+ $new->uri = $uri;
117
+
118
+ if (!$preserveHost || !isset($this->headerNames['host'])) {
119
+ $new->updateHostFromUri();
120
+ }
121
+
122
+ return $new;
123
+ }
124
+
125
+ private function updateHostFromUri(): void
126
+ {
127
+ $host = $this->uri->getHost();
128
+
129
+ if ($host == '') {
130
+ return;
131
+ }
132
+
133
+ if (($port = $this->uri->getPort()) !== null) {
134
+ $host .= ':' . $port;
135
+ }
136
+
137
+ if (isset($this->headerNames['host'])) {
138
+ $header = $this->headerNames['host'];
139
+ } else {
140
+ $header = 'Host';
141
+ $this->headerNames['host'] = 'Host';
142
+ }
143
+ // Ensure Host is the first header.
144
+ // See: http://tools.ietf.org/html/rfc7230#section-5.4
145
+ $this->headers = [$header => [$host]] + $this->headers;
146
+ }
147
+
148
+ /**
149
+ * @param mixed $method
150
+ */
151
+ private function assertMethod($method): void
152
+ {
153
+ if (!is_string($method) || $method === '') {
154
+ throw new InvalidArgumentException('Method must be a non-empty string.');
155
+ }
156
+ }
157
+ }
src/vendor/guzzlehttp/psr7/src/Response.php ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\ResponseInterface;
8
+ use Psr\Http\Message\StreamInterface;
9
+
10
+ /**
11
+ * PSR-7 response implementation.
12
+ */
13
+ class Response implements ResponseInterface
14
+ {
15
+ use MessageTrait;
16
+
17
+ /** Map of standard HTTP status code/reason phrases */
18
+ private const PHRASES = [
19
+ 100 => 'Continue',
20
+ 101 => 'Switching Protocols',
21
+ 102 => 'Processing',
22
+ 200 => 'OK',
23
+ 201 => 'Created',
24
+ 202 => 'Accepted',
25
+ 203 => 'Non-Authoritative Information',
26
+ 204 => 'No Content',
27
+ 205 => 'Reset Content',
28
+ 206 => 'Partial Content',
29
+ 207 => 'Multi-status',
30
+ 208 => 'Already Reported',
31
+ 300 => 'Multiple Choices',
32
+ 301 => 'Moved Permanently',
33
+ 302 => 'Found',
34
+ 303 => 'See Other',
35
+ 304 => 'Not Modified',
36
+ 305 => 'Use Proxy',
37
+ 306 => 'Switch Proxy',
38
+ 307 => 'Temporary Redirect',
39
+ 308 => 'Permanent Redirect',
40
+ 400 => 'Bad Request',
41
+ 401 => 'Unauthorized',
42
+ 402 => 'Payment Required',
43
+ 403 => 'Forbidden',
44
+ 404 => 'Not Found',
45
+ 405 => 'Method Not Allowed',
46
+ 406 => 'Not Acceptable',
47
+ 407 => 'Proxy Authentication Required',
48
+ 408 => 'Request Time-out',
49
+ 409 => 'Conflict',
50
+ 410 => 'Gone',
51
+ 411 => 'Length Required',
52
+ 412 => 'Precondition Failed',
53
+ 413 => 'Request Entity Too Large',
54
+ 414 => 'Request-URI Too Large',
55
+ 415 => 'Unsupported Media Type',
56
+ 416 => 'Requested range not satisfiable',
57
+ 417 => 'Expectation Failed',
58
+ 418 => 'I\'m a teapot',
59
+ 422 => 'Unprocessable Entity',
60
+ 423 => 'Locked',
61
+ 424 => 'Failed Dependency',
62
+ 425 => 'Unordered Collection',
63
+ 426 => 'Upgrade Required',
64
+ 428 => 'Precondition Required',
65
+ 429 => 'Too Many Requests',
66
+ 431 => 'Request Header Fields Too Large',
67
+ 451 => 'Unavailable For Legal Reasons',
68
+ 500 => 'Internal Server Error',
69
+ 501 => 'Not Implemented',
70
+ 502 => 'Bad Gateway',
71
+ 503 => 'Service Unavailable',
72
+ 504 => 'Gateway Time-out',
73
+ 505 => 'HTTP Version not supported',
74
+ 506 => 'Variant Also Negotiates',
75
+ 507 => 'Insufficient Storage',
76
+ 508 => 'Loop Detected',
77
+ 510 => 'Not Extended',
78
+ 511 => 'Network Authentication Required',
79
+ ];
80
+
81
+ /** @var string */
82
+ private $reasonPhrase;
83
+
84
+ /** @var int */
85
+ private $statusCode;
86
+
87
+ /**
88
+ * @param int $status Status code
89
+ * @param array<string, string|string[]> $headers Response headers
90
+ * @param string|resource|StreamInterface|null $body Response body
91
+ * @param string $version Protocol version
92
+ * @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
93
+ */
94
+ public function __construct(
95
+ int $status = 200,
96
+ array $headers = [],
97
+ $body = null,
98
+ string $version = '1.1',
99
+ string $reason = null
100
+ ) {
101
+ $this->assertStatusCodeRange($status);
102
+
103
+ $this->statusCode = $status;
104
+
105
+ if ($body !== '' && $body !== null) {
106
+ $this->stream = Utils::streamFor($body);
107
+ }
108
+
109
+ $this->setHeaders($headers);
110
+ if ($reason == '' && isset(self::PHRASES[$this->statusCode])) {
111
+ $this->reasonPhrase = self::PHRASES[$this->statusCode];
112
+ } else {
113
+ $this->reasonPhrase = (string) $reason;
114
+ }
115
+
116
+ $this->protocol = $version;
117
+ }
118
+
119
+ public function getStatusCode(): int
120
+ {
121
+ return $this->statusCode;
122
+ }
123
+
124
+ public function getReasonPhrase(): string
125
+ {
126
+ return $this->reasonPhrase;
127
+ }
128
+
129
+ public function withStatus($code, $reasonPhrase = ''): ResponseInterface
130
+ {
131
+ $this->assertStatusCodeIsInteger($code);
132
+ $code = (int) $code;
133
+ $this->assertStatusCodeRange($code);
134
+
135
+ $new = clone $this;
136
+ $new->statusCode = $code;
137
+ if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) {
138
+ $reasonPhrase = self::PHRASES[$new->statusCode];
139
+ }
140
+ $new->reasonPhrase = (string) $reasonPhrase;
141
+ return $new;
142
+ }
143
+
144
+ /**
145
+ * @param mixed $statusCode
146
+ */
147
+ private function assertStatusCodeIsInteger($statusCode): void
148
+ {
149
+ if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) {
150
+ throw new \InvalidArgumentException('Status code must be an integer value.');
151
+ }
152
+ }
153
+
154
+ private function assertStatusCodeRange(int $statusCode): void
155
+ {
156
+ if ($statusCode < 100 || $statusCode >= 600) {
157
+ throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.');
158
+ }
159
+ }
160
+ }
src/vendor/guzzlehttp/psr7/src/Rfc7230.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ /**
8
+ * @internal
9
+ */
10
+ final class Rfc7230
11
+ {
12
+ /**
13
+ * Header related regular expressions (based on amphp/http package)
14
+ *
15
+ * Note: header delimiter (\r\n) is modified to \r?\n to accept line feed only delimiters for BC reasons.
16
+ *
17
+ * @link https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15
18
+ *
19
+ * @license https://github.com/amphp/http/blob/v1.0.1/LICENSE
20
+ */
21
+ public const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m";
22
+ public const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)";
23
+ }
src/vendor/guzzlehttp/psr7/src/ServerRequest.php ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use InvalidArgumentException;
8
+ use Psr\Http\Message\ServerRequestInterface;
9
+ use Psr\Http\Message\StreamInterface;
10
+ use Psr\Http\Message\UploadedFileInterface;
11
+ use Psr\Http\Message\UriInterface;
12
+
13
+ /**
14
+ * Server-side HTTP request
15
+ *
16
+ * Extends the Request definition to add methods for accessing incoming data,
17
+ * specifically server parameters, cookies, matched path parameters, query
18
+ * string arguments, body parameters, and upload file information.
19
+ *
20
+ * "Attributes" are discovered via decomposing the request (and usually
21
+ * specifically the URI path), and typically will be injected by the application.
22
+ *
23
+ * Requests are considered immutable; all methods that might change state are
24
+ * implemented such that they retain the internal state of the current
25
+ * message and return a new instance that contains the changed state.
26
+ */
27
+ class ServerRequest extends Request implements ServerRequestInterface
28
+ {
29
+ /**
30
+ * @var array
31
+ */
32
+ private $attributes = [];
33
+
34
+ /**
35
+ * @var array
36
+ */
37
+ private $cookieParams = [];
38
+
39
+ /**
40
+ * @var array|object|null
41
+ */
42
+ private $parsedBody;
43
+
44
+ /**
45
+ * @var array
46
+ */
47
+ private $queryParams = [];
48
+
49
+ /**
50
+ * @var array
51
+ */
52
+ private $serverParams;
53
+
54
+ /**
55
+ * @var array
56
+ */
57
+ private $uploadedFiles = [];
58
+
59
+ /**
60
+ * @param string $method HTTP method
61
+ * @param string|UriInterface $uri URI
62
+ * @param array<string, string|string[]> $headers Request headers
63
+ * @param string|resource|StreamInterface|null $body Request body
64
+ * @param string $version Protocol version
65
+ * @param array $serverParams Typically the $_SERVER superglobal
66
+ */
67
+ public function __construct(
68
+ string $method,
69
+ $uri,
70
+ array $headers = [],
71
+ $body = null,
72
+ string $version = '1.1',
73
+ array $serverParams = []
74
+ ) {
75
+ $this->serverParams = $serverParams;
76
+
77
+ parent::__construct($method, $uri, $headers, $body, $version);
78
+ }
79
+
80
+ /**
81
+ * Return an UploadedFile instance array.
82
+ *
83
+ * @param array $files An array which respect $_FILES structure
84
+ *
85
+ * @throws InvalidArgumentException for unrecognized values
86
+ */
87
+ public static function normalizeFiles(array $files): array
88
+ {
89
+ $normalized = [];
90
+
91
+ foreach ($files as $key => $value) {
92
+ if ($value instanceof UploadedFileInterface) {
93
+ $normalized[$key] = $value;
94
+ } elseif (is_array($value) && isset($value['tmp_name'])) {
95
+ $normalized[$key] = self::createUploadedFileFromSpec($value);
96
+ } elseif (is_array($value)) {
97
+ $normalized[$key] = self::normalizeFiles($value);
98
+ continue;
99
+ } else {
100
+ throw new InvalidArgumentException('Invalid value in files specification');
101
+ }
102
+ }
103
+
104
+ return $normalized;
105
+ }
106
+
107
+ /**
108
+ * Create and return an UploadedFile instance from a $_FILES specification.
109
+ *
110
+ * If the specification represents an array of values, this method will
111
+ * delegate to normalizeNestedFileSpec() and return that return value.
112
+ *
113
+ * @param array $value $_FILES struct
114
+ *
115
+ * @return UploadedFileInterface|UploadedFileInterface[]
116
+ */
117
+ private static function createUploadedFileFromSpec(array $value)
118
+ {
119
+ if (is_array($value['tmp_name'])) {
120
+ return self::normalizeNestedFileSpec($value);
121
+ }
122
+
123
+ return new UploadedFile(
124
+ $value['tmp_name'],
125
+ (int) $value['size'],
126
+ (int) $value['error'],
127
+ $value['name'],
128
+ $value['type']
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Normalize an array of file specifications.
134
+ *
135
+ * Loops through all nested files and returns a normalized array of
136
+ * UploadedFileInterface instances.
137
+ *
138
+ * @return UploadedFileInterface[]
139
+ */
140
+ private static function normalizeNestedFileSpec(array $files = []): array
141
+ {
142
+ $normalizedFiles = [];
143
+
144
+ foreach (array_keys($files['tmp_name']) as $key) {
145
+ $spec = [
146
+ 'tmp_name' => $files['tmp_name'][$key],
147
+ 'size' => $files['size'][$key],
148
+ 'error' => $files['error'][$key],
149
+ 'name' => $files['name'][$key],
150
+ 'type' => $files['type'][$key],
151
+ ];
152
+ $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
153
+ }
154
+
155
+ return $normalizedFiles;
156
+ }
157
+
158
+ /**
159
+ * Return a ServerRequest populated with superglobals:
160
+ * $_GET
161
+ * $_POST
162
+ * $_COOKIE
163
+ * $_FILES
164
+ * $_SERVER
165
+ */
166
+ public static function fromGlobals(): ServerRequestInterface
167
+ {
168
+ $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
169
+ $headers = getallheaders();
170
+ $uri = self::getUriFromGlobals();
171
+ $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
172
+ $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
173
+
174
+ $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
175
+
176
+ return $serverRequest
177
+ ->withCookieParams($_COOKIE)
178
+ ->withQueryParams($_GET)
179
+ ->withParsedBody($_POST)
180
+ ->withUploadedFiles(self::normalizeFiles($_FILES));
181
+ }
182
+
183
+ private static function extractHostAndPortFromAuthority(string $authority): array
184
+ {
185
+ $uri = 'http://' . $authority;
186
+ $parts = parse_url($uri);
187
+ if (false === $parts) {
188
+ return [null, null];
189
+ }
190
+
191
+ $host = $parts['host'] ?? null;
192
+ $port = $parts['port'] ?? null;
193
+
194
+ return [$host, $port];
195
+ }
196
+
197
+ /**
198
+ * Get a Uri populated with values from $_SERVER.
199
+ */
200
+ public static function getUriFromGlobals(): UriInterface
201
+ {
202
+ $uri = new Uri('');
203
+
204
+ $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
205
+
206
+ $hasPort = false;
207
+ if (isset($_SERVER['HTTP_HOST'])) {
208
+ [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);
209
+ if ($host !== null) {
210
+ $uri = $uri->withHost($host);
211
+ }
212
+
213
+ if ($port !== null) {
214
+ $hasPort = true;
215
+ $uri = $uri->withPort($port);
216
+ }
217
+ } elseif (isset($_SERVER['SERVER_NAME'])) {
218
+ $uri = $uri->withHost($_SERVER['SERVER_NAME']);
219
+ } elseif (isset($_SERVER['SERVER_ADDR'])) {
220
+ $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
221
+ }
222
+
223
+ if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
224
+ $uri = $uri->withPort($_SERVER['SERVER_PORT']);
225
+ }
226
+
227
+ $hasQuery = false;
228
+ if (isset($_SERVER['REQUEST_URI'])) {
229
+ $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
230
+ $uri = $uri->withPath($requestUriParts[0]);
231
+ if (isset($requestUriParts[1])) {
232
+ $hasQuery = true;
233
+ $uri = $uri->withQuery($requestUriParts[1]);
234
+ }
235
+ }
236
+
237
+ if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
238
+ $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
239
+ }
240
+
241
+ return $uri;
242
+ }
243
+
244
+ public function getServerParams(): array
245
+ {
246
+ return $this->serverParams;
247
+ }
248
+
249
+ public function getUploadedFiles(): array
250
+ {
251
+ return $this->uploadedFiles;
252
+ }
253
+
254
+ public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
255
+ {
256
+ $new = clone $this;
257
+ $new->uploadedFiles = $uploadedFiles;
258
+
259
+ return $new;
260
+ }
261
+
262
+ public function getCookieParams(): array
263
+ {
264
+ return $this->cookieParams;
265
+ }
266
+
267
+ public function withCookieParams(array $cookies): ServerRequestInterface
268
+ {
269
+ $new = clone $this;
270
+ $new->cookieParams = $cookies;
271
+
272
+ return $new;
273
+ }
274
+
275
+ public function getQueryParams(): array
276
+ {
277
+ return $this->queryParams;
278
+ }
279
+
280
+ public function withQueryParams(array $query): ServerRequestInterface
281
+ {
282
+ $new = clone $this;
283
+ $new->queryParams = $query;
284
+
285
+ return $new;
286
+ }
287
+
288
+ /**
289
+ * {@inheritdoc}
290
+ *
291
+ * @return array|object|null
292
+ */
293
+ public function getParsedBody()
294
+ {
295
+ return $this->parsedBody;
296
+ }
297
+
298
+ public function withParsedBody($data): ServerRequestInterface
299
+ {
300
+ $new = clone $this;
301
+ $new->parsedBody = $data;
302
+
303
+ return $new;
304
+ }
305
+
306
+ public function getAttributes(): array
307
+ {
308
+ return $this->attributes;
309
+ }
310
+
311
+ /**
312
+ * {@inheritdoc}
313
+ *
314
+ * @return mixed
315
+ */
316
+ public function getAttribute($attribute, $default = null)
317
+ {
318
+ if (false === array_key_exists($attribute, $this->attributes)) {
319
+ return $default;
320
+ }
321
+
322
+ return $this->attributes[$attribute];
323
+ }
324
+
325
+ public function withAttribute($attribute, $value): ServerRequestInterface
326
+ {
327
+ $new = clone $this;
328
+ $new->attributes[$attribute] = $value;
329
+
330
+ return $new;
331
+ }
332
+
333
+ public function withoutAttribute($attribute): ServerRequestInterface
334
+ {
335
+ if (false === array_key_exists($attribute, $this->attributes)) {
336
+ return $this;
337
+ }
338
+
339
+ $new = clone $this;
340
+ unset($new->attributes[$attribute]);
341
+
342
+ return $new;
343
+ }
344
+ }
src/vendor/guzzlehttp/psr7/src/Stream.php ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * PHP stream implementation.
11
+ */
12
+ class Stream implements StreamInterface
13
+ {
14
+ /**
15
+ * @see http://php.net/manual/function.fopen.php
16
+ * @see http://php.net/manual/en/function.gzopen.php
17
+ */
18
+ private const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/';
19
+ private const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/';
20
+
21
+ /** @var resource */
22
+ private $stream;
23
+ /** @var int|null */
24
+ private $size;
25
+ /** @var bool */
26
+ private $seekable;
27
+ /** @var bool */
28
+ private $readable;
29
+ /** @var bool */
30
+ private $writable;
31
+ /** @var string|null */
32
+ private $uri;
33
+ /** @var mixed[] */
34
+ private $customMetadata;
35
+
36
+ /**
37
+ * This constructor accepts an associative array of options.
38
+ *
39
+ * - size: (int) If a read stream would otherwise have an indeterminate
40
+ * size, but the size is known due to foreknowledge, then you can
41
+ * provide that size, in bytes.
42
+ * - metadata: (array) Any additional metadata to return when the metadata
43
+ * of the stream is accessed.
44
+ *
45
+ * @param resource $stream Stream resource to wrap.
46
+ * @param array{size?: int, metadata?: array} $options Associative array of options.
47
+ *
48
+ * @throws \InvalidArgumentException if the stream is not a stream resource
49
+ */
50
+ public function __construct($stream, array $options = [])
51
+ {
52
+ if (!is_resource($stream)) {
53
+ throw new \InvalidArgumentException('Stream must be a resource');
54
+ }
55
+
56
+ if (isset($options['size'])) {
57
+ $this->size = $options['size'];
58
+ }
59
+
60
+ $this->customMetadata = $options['metadata'] ?? [];
61
+ $this->stream = $stream;
62
+ $meta = stream_get_meta_data($this->stream);
63
+ $this->seekable = $meta['seekable'];
64
+ $this->readable = (bool)preg_match(self::READABLE_MODES, $meta['mode']);
65
+ $this->writable = (bool)preg_match(self::WRITABLE_MODES, $meta['mode']);
66
+ $this->uri = $this->getMetadata('uri');
67
+ }
68
+
69
+ /**
70
+ * Closes the stream when the destructed
71
+ */
72
+ public function __destruct()
73
+ {
74
+ $this->close();
75
+ }
76
+
77
+ public function __toString(): string
78
+ {
79
+ try {
80
+ if ($this->isSeekable()) {
81
+ $this->seek(0);
82
+ }
83
+ return $this->getContents();
84
+ } catch (\Throwable $e) {
85
+ if (\PHP_VERSION_ID >= 70400) {
86
+ throw $e;
87
+ }
88
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
89
+ return '';
90
+ }
91
+ }
92
+
93
+ public function getContents(): string
94
+ {
95
+ if (!isset($this->stream)) {
96
+ throw new \RuntimeException('Stream is detached');
97
+ }
98
+
99
+ if (!$this->readable) {
100
+ throw new \RuntimeException('Cannot read from non-readable stream');
101
+ }
102
+
103
+ return Utils::tryGetContents($this->stream);
104
+ }
105
+
106
+ public function close(): void
107
+ {
108
+ if (isset($this->stream)) {
109
+ if (is_resource($this->stream)) {
110
+ fclose($this->stream);
111
+ }
112
+ $this->detach();
113
+ }
114
+ }
115
+
116
+ public function detach()
117
+ {
118
+ if (!isset($this->stream)) {
119
+ return null;
120
+ }
121
+
122
+ $result = $this->stream;
123
+ unset($this->stream);
124
+ $this->size = $this->uri = null;
125
+ $this->readable = $this->writable = $this->seekable = false;
126
+
127
+ return $result;
128
+ }
129
+
130
+ public function getSize(): ?int
131
+ {
132
+ if ($this->size !== null) {
133
+ return $this->size;
134
+ }
135
+
136
+ if (!isset($this->stream)) {
137
+ return null;
138
+ }
139
+
140
+ // Clear the stat cache if the stream has a URI
141
+ if ($this->uri) {
142
+ clearstatcache(true, $this->uri);
143
+ }
144
+
145
+ $stats = fstat($this->stream);
146
+ if (is_array($stats) && isset($stats['size'])) {
147
+ $this->size = $stats['size'];
148
+ return $this->size;
149
+ }
150
+
151
+ return null;
152
+ }
153
+
154
+ public function isReadable(): bool
155
+ {
156
+ return $this->readable;
157
+ }
158
+
159
+ public function isWritable(): bool
160
+ {
161
+ return $this->writable;
162
+ }
163
+
164
+ public function isSeekable(): bool
165
+ {
166
+ return $this->seekable;
167
+ }
168
+
169
+ public function eof(): bool
170
+ {
171
+ if (!isset($this->stream)) {
172
+ throw new \RuntimeException('Stream is detached');
173
+ }
174
+
175
+ return feof($this->stream);
176
+ }
177
+
178
+ public function tell(): int
179
+ {
180
+ if (!isset($this->stream)) {
181
+ throw new \RuntimeException('Stream is detached');
182
+ }
183
+
184
+ $result = ftell($this->stream);
185
+
186
+ if ($result === false) {
187
+ throw new \RuntimeException('Unable to determine stream position');
188
+ }
189
+
190
+ return $result;
191
+ }
192
+
193
+ public function rewind(): void
194
+ {
195
+ $this->seek(0);
196
+ }
197
+
198
+ public function seek($offset, $whence = SEEK_SET): void
199
+ {
200
+ $whence = (int) $whence;
201
+
202
+ if (!isset($this->stream)) {
203
+ throw new \RuntimeException('Stream is detached');
204
+ }
205
+ if (!$this->seekable) {
206
+ throw new \RuntimeException('Stream is not seekable');
207
+ }
208
+ if (fseek($this->stream, $offset, $whence) === -1) {
209
+ throw new \RuntimeException('Unable to seek to stream position '
210
+ . $offset . ' with whence ' . var_export($whence, true));
211
+ }
212
+ }
213
+
214
+ public function read($length): string
215
+ {
216
+ if (!isset($this->stream)) {
217
+ throw new \RuntimeException('Stream is detached');
218
+ }
219
+ if (!$this->readable) {
220
+ throw new \RuntimeException('Cannot read from non-readable stream');
221
+ }
222
+ if ($length < 0) {
223
+ throw new \RuntimeException('Length parameter cannot be negative');
224
+ }
225
+
226
+ if (0 === $length) {
227
+ return '';
228
+ }
229
+
230
+ try {
231
+ $string = fread($this->stream, $length);
232
+ } catch (\Exception $e) {
233
+ throw new \RuntimeException('Unable to read from stream', 0, $e);
234
+ }
235
+
236
+ if (false === $string) {
237
+ throw new \RuntimeException('Unable to read from stream');
238
+ }
239
+
240
+ return $string;
241
+ }
242
+
243
+ public function write($string): int
244
+ {
245
+ if (!isset($this->stream)) {
246
+ throw new \RuntimeException('Stream is detached');
247
+ }
248
+ if (!$this->writable) {
249
+ throw new \RuntimeException('Cannot write to a non-writable stream');
250
+ }
251
+
252
+ // We can't know the size after writing anything
253
+ $this->size = null;
254
+ $result = fwrite($this->stream, $string);
255
+
256
+ if ($result === false) {
257
+ throw new \RuntimeException('Unable to write to stream');
258
+ }
259
+
260
+ return $result;
261
+ }
262
+
263
+ /**
264
+ * {@inheritdoc}
265
+ *
266
+ * @return mixed
267
+ */
268
+ public function getMetadata($key = null)
269
+ {
270
+ if (!isset($this->stream)) {
271
+ return $key ? null : [];
272
+ } elseif (!$key) {
273
+ return $this->customMetadata + stream_get_meta_data($this->stream);
274
+ } elseif (isset($this->customMetadata[$key])) {
275
+ return $this->customMetadata[$key];
276
+ }
277
+
278
+ $meta = stream_get_meta_data($this->stream);
279
+
280
+ return $meta[$key] ?? null;
281
+ }
282
+ }
src/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Stream decorator trait
11
+ *
12
+ * @property StreamInterface $stream
13
+ */
14
+ trait StreamDecoratorTrait
15
+ {
16
+ /**
17
+ * @param StreamInterface $stream Stream to decorate
18
+ */
19
+ public function __construct(StreamInterface $stream)
20
+ {
21
+ $this->stream = $stream;
22
+ }
23
+
24
+ /**
25
+ * Magic method used to create a new stream if streams are not added in
26
+ * the constructor of a decorator (e.g., LazyOpenStream).
27
+ *
28
+ * @return StreamInterface
29
+ */
30
+ public function __get(string $name)
31
+ {
32
+ if ($name === 'stream') {
33
+ $this->stream = $this->createStream();
34
+ return $this->stream;
35
+ }
36
+
37
+ throw new \UnexpectedValueException("$name not found on class");
38
+ }
39
+
40
+ public function __toString(): string
41
+ {
42
+ try {
43
+ if ($this->isSeekable()) {
44
+ $this->seek(0);
45
+ }
46
+ return $this->getContents();
47
+ } catch (\Throwable $e) {
48
+ if (\PHP_VERSION_ID >= 70400) {
49
+ throw $e;
50
+ }
51
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
52
+ return '';
53
+ }
54
+ }
55
+
56
+ public function getContents(): string
57
+ {
58
+ return Utils::copyToString($this);
59
+ }
60
+
61
+ /**
62
+ * Allow decorators to implement custom methods
63
+ *
64
+ * @return mixed
65
+ */
66
+ public function __call(string $method, array $args)
67
+ {
68
+ /** @var callable $callable */
69
+ $callable = [$this->stream, $method];
70
+ $result = call_user_func_array($callable, $args);
71
+
72
+ // Always return the wrapped object if the result is a return $this
73
+ return $result === $this->stream ? $this : $result;
74
+ }
75
+
76
+ public function close(): void
77
+ {
78
+ $this->stream->close();
79
+ }
80
+
81
+ /**
82
+ * {@inheritdoc}
83
+ *
84
+ * @return mixed
85
+ */
86
+ public function getMetadata($key = null)
87
+ {
88
+ return $this->stream->getMetadata($key);
89
+ }
90
+
91
+ public function detach()
92
+ {
93
+ return $this->stream->detach();
94
+ }
95
+
96
+ public function getSize(): ?int
97
+ {
98
+ return $this->stream->getSize();
99
+ }
100
+
101
+ public function eof(): bool
102
+ {
103
+ return $this->stream->eof();
104
+ }
105
+
106
+ public function tell(): int
107
+ {
108
+ return $this->stream->tell();
109
+ }
110
+
111
+ public function isReadable(): bool
112
+ {
113
+ return $this->stream->isReadable();
114
+ }
115
+
116
+ public function isWritable(): bool
117
+ {
118
+ return $this->stream->isWritable();
119
+ }
120
+
121
+ public function isSeekable(): bool
122
+ {
123
+ return $this->stream->isSeekable();
124
+ }
125
+
126
+ public function rewind(): void
127
+ {
128
+ $this->seek(0);
129
+ }
130
+
131
+ public function seek($offset, $whence = SEEK_SET): void
132
+ {
133
+ $this->stream->seek($offset, $whence);
134
+ }
135
+
136
+ public function read($length): string
137
+ {
138
+ return $this->stream->read($length);
139
+ }
140
+
141
+ public function write($string): int
142
+ {
143
+ return $this->stream->write($string);
144
+ }
145
+
146
+ /**
147
+ * Implement in subclasses to dynamically create streams when requested.
148
+ *
149
+ * @throws \BadMethodCallException
150
+ */
151
+ protected function createStream(): StreamInterface
152
+ {
153
+ throw new \BadMethodCallException('Not implemented');
154
+ }
155
+ }
src/vendor/guzzlehttp/psr7/src/StreamWrapper.php ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Converts Guzzle streams into PHP stream resources.
11
+ *
12
+ * @see https://www.php.net/streamwrapper
13
+ */
14
+ final class StreamWrapper
15
+ {
16
+ /** @var resource */
17
+ public $context;
18
+
19
+ /** @var StreamInterface */
20
+ private $stream;
21
+
22
+ /** @var string r, r+, or w */
23
+ private $mode;
24
+
25
+ /**
26
+ * Returns a resource representing the stream.
27
+ *
28
+ * @param StreamInterface $stream The stream to get a resource for
29
+ *
30
+ * @return resource
31
+ *
32
+ * @throws \InvalidArgumentException if stream is not readable or writable
33
+ */
34
+ public static function getResource(StreamInterface $stream)
35
+ {
36
+ self::register();
37
+
38
+ if ($stream->isReadable()) {
39
+ $mode = $stream->isWritable() ? 'r+' : 'r';
40
+ } elseif ($stream->isWritable()) {
41
+ $mode = 'w';
42
+ } else {
43
+ throw new \InvalidArgumentException('The stream must be readable, '
44
+ . 'writable, or both.');
45
+ }
46
+
47
+ return fopen('guzzle://stream', $mode, false, self::createStreamContext($stream));
48
+ }
49
+
50
+ /**
51
+ * Creates a stream context that can be used to open a stream as a php stream resource.
52
+ *
53
+ * @return resource
54
+ */
55
+ public static function createStreamContext(StreamInterface $stream)
56
+ {
57
+ return stream_context_create([
58
+ 'guzzle' => ['stream' => $stream]
59
+ ]);
60
+ }
61
+
62
+ /**
63
+ * Registers the stream wrapper if needed
64
+ */
65
+ public static function register(): void
66
+ {
67
+ if (!in_array('guzzle', stream_get_wrappers())) {
68
+ stream_wrapper_register('guzzle', __CLASS__);
69
+ }
70
+ }
71
+
72
+ public function stream_open(string $path, string $mode, int $options, string &$opened_path = null): bool
73
+ {
74
+ $options = stream_context_get_options($this->context);
75
+
76
+ if (!isset($options['guzzle']['stream'])) {
77
+ return false;
78
+ }
79
+
80
+ $this->mode = $mode;
81
+ $this->stream = $options['guzzle']['stream'];
82
+
83
+ return true;
84
+ }
85
+
86
+ public function stream_read(int $count): string
87
+ {
88
+ return $this->stream->read($count);
89
+ }
90
+
91
+ public function stream_write(string $data): int
92
+ {
93
+ return $this->stream->write($data);
94
+ }
95
+
96
+ public function stream_tell(): int
97
+ {
98
+ return $this->stream->tell();
99
+ }
100
+
101
+ public function stream_eof(): bool
102
+ {
103
+ return $this->stream->eof();
104
+ }
105
+
106
+ public function stream_seek(int $offset, int $whence): bool
107
+ {
108
+ $this->stream->seek($offset, $whence);
109
+
110
+ return true;
111
+ }
112
+
113
+ /**
114
+ * @return resource|false
115
+ */
116
+ public function stream_cast(int $cast_as)
117
+ {
118
+ $stream = clone($this->stream);
119
+ $resource = $stream->detach();
120
+
121
+ return $resource ?? false;
122
+ }
123
+
124
+ /**
125
+ * @return array<int|string, int>
126
+ */
127
+ public function stream_stat(): array
128
+ {
129
+ static $modeMap = [
130
+ 'r' => 33060,
131
+ 'rb' => 33060,
132
+ 'r+' => 33206,
133
+ 'w' => 33188,
134
+ 'wb' => 33188
135
+ ];
136
+
137
+ return [
138
+ 'dev' => 0,
139
+ 'ino' => 0,
140
+ 'mode' => $modeMap[$this->mode],
141
+ 'nlink' => 0,
142
+ 'uid' => 0,
143
+ 'gid' => 0,
144
+ 'rdev' => 0,
145
+ 'size' => $this->stream->getSize() ?: 0,
146
+ 'atime' => 0,
147
+ 'mtime' => 0,
148
+ 'ctime' => 0,
149
+ 'blksize' => 0,
150
+ 'blocks' => 0
151
+ ];
152
+ }
153
+
154
+ /**
155
+ * @return array<int|string, int>
156
+ */
157
+ public function url_stat(string $path, int $flags): array
158
+ {
159
+ return [
160
+ 'dev' => 0,
161
+ 'ino' => 0,
162
+ 'mode' => 0,
163
+ 'nlink' => 0,
164
+ 'uid' => 0,
165
+ 'gid' => 0,
166
+ 'rdev' => 0,
167
+ 'size' => 0,
168
+ 'atime' => 0,
169
+ 'mtime' => 0,
170
+ 'ctime' => 0,
171
+ 'blksize' => 0,
172
+ 'blocks' => 0
173
+ ];
174
+ }
175
+ }
src/vendor/guzzlehttp/psr7/src/UploadedFile.php ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use InvalidArgumentException;
8
+ use Psr\Http\Message\StreamInterface;
9
+ use Psr\Http\Message\UploadedFileInterface;
10
+ use RuntimeException;
11
+
12
+ class UploadedFile implements UploadedFileInterface
13
+ {
14
+ private const ERRORS = [
15
+ UPLOAD_ERR_OK,
16
+ UPLOAD_ERR_INI_SIZE,
17
+ UPLOAD_ERR_FORM_SIZE,
18
+ UPLOAD_ERR_PARTIAL,
19
+ UPLOAD_ERR_NO_FILE,
20
+ UPLOAD_ERR_NO_TMP_DIR,
21
+ UPLOAD_ERR_CANT_WRITE,
22
+ UPLOAD_ERR_EXTENSION,
23
+ ];
24
+
25
+ /**
26
+ * @var string|null
27
+ */
28
+ private $clientFilename;
29
+
30
+ /**
31
+ * @var string|null
32
+ */
33
+ private $clientMediaType;
34
+
35
+ /**
36
+ * @var int
37
+ */
38
+ private $error;
39
+
40
+ /**
41
+ * @var string|null
42
+ */
43
+ private $file;
44
+
45
+ /**
46
+ * @var bool
47
+ */
48
+ private $moved = false;
49
+
50
+ /**
51
+ * @var int|null
52
+ */
53
+ private $size;
54
+
55
+ /**
56
+ * @var StreamInterface|null
57
+ */
58
+ private $stream;
59
+
60
+ /**
61
+ * @param StreamInterface|string|resource $streamOrFile
62
+ */
63
+ public function __construct(
64
+ $streamOrFile,
65
+ ?int $size,
66
+ int $errorStatus,
67
+ string $clientFilename = null,
68
+ string $clientMediaType = null
69
+ ) {
70
+ $this->setError($errorStatus);
71
+ $this->size = $size;
72
+ $this->clientFilename = $clientFilename;
73
+ $this->clientMediaType = $clientMediaType;
74
+
75
+ if ($this->isOk()) {
76
+ $this->setStreamOrFile($streamOrFile);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Depending on the value set file or stream variable
82
+ *
83
+ * @param StreamInterface|string|resource $streamOrFile
84
+ *
85
+ * @throws InvalidArgumentException
86
+ */
87
+ private function setStreamOrFile($streamOrFile): void
88
+ {
89
+ if (is_string($streamOrFile)) {
90
+ $this->file = $streamOrFile;
91
+ } elseif (is_resource($streamOrFile)) {
92
+ $this->stream = new Stream($streamOrFile);
93
+ } elseif ($streamOrFile instanceof StreamInterface) {
94
+ $this->stream = $streamOrFile;
95
+ } else {
96
+ throw new InvalidArgumentException(
97
+ 'Invalid stream or file provided for UploadedFile'
98
+ );
99
+ }
100
+ }
101
+
102
+ /**
103
+ * @throws InvalidArgumentException
104
+ */
105
+ private function setError(int $error): void
106
+ {
107
+ if (false === in_array($error, UploadedFile::ERRORS, true)) {
108
+ throw new InvalidArgumentException(
109
+ 'Invalid error status for UploadedFile'
110
+ );
111
+ }
112
+
113
+ $this->error = $error;
114
+ }
115
+
116
+ private function isStringNotEmpty($param): bool
117
+ {
118
+ return is_string($param) && false === empty($param);
119
+ }
120
+
121
+ /**
122
+ * Return true if there is no upload error
123
+ */
124
+ private function isOk(): bool
125
+ {
126
+ return $this->error === UPLOAD_ERR_OK;
127
+ }
128
+
129
+ public function isMoved(): bool
130
+ {
131
+ return $this->moved;
132
+ }
133
+
134
+ /**
135
+ * @throws RuntimeException if is moved or not ok
136
+ */
137
+ private function validateActive(): void
138
+ {
139
+ if (false === $this->isOk()) {
140
+ throw new RuntimeException('Cannot retrieve stream due to upload error');
141
+ }
142
+
143
+ if ($this->isMoved()) {
144
+ throw new RuntimeException('Cannot retrieve stream after it has already been moved');
145
+ }
146
+ }
147
+
148
+ public function getStream(): StreamInterface
149
+ {
150
+ $this->validateActive();
151
+
152
+ if ($this->stream instanceof StreamInterface) {
153
+ return $this->stream;
154
+ }
155
+
156
+ /** @var string $file */
157
+ $file = $this->file;
158
+
159
+ return new LazyOpenStream($file, 'r+');
160
+ }
161
+
162
+ public function moveTo($targetPath): void
163
+ {
164
+ $this->validateActive();
165
+
166
+ if (false === $this->isStringNotEmpty($targetPath)) {
167
+ throw new InvalidArgumentException(
168
+ 'Invalid path provided for move operation; must be a non-empty string'
169
+ );
170
+ }
171
+
172
+ if ($this->file) {
173
+ $this->moved = PHP_SAPI === 'cli'
174
+ ? rename($this->file, $targetPath)
175
+ : move_uploaded_file($this->file, $targetPath);
176
+ } else {
177
+ Utils::copyToStream(
178
+ $this->getStream(),
179
+ new LazyOpenStream($targetPath, 'w')
180
+ );
181
+
182
+ $this->moved = true;
183
+ }
184
+
185
+ if (false === $this->moved) {
186
+ throw new RuntimeException(
187
+ sprintf('Uploaded file could not be moved to %s', $targetPath)
188
+ );
189
+ }
190
+ }
191
+
192
+ public function getSize(): ?int
193
+ {
194
+ return $this->size;
195
+ }
196
+
197
+ public function getError(): int
198
+ {
199
+ return $this->error;
200
+ }
201
+
202
+ public function getClientFilename(): ?string
203
+ {
204
+ return $this->clientFilename;
205
+ }
206
+
207
+ public function getClientMediaType(): ?string
208
+ {
209
+ return $this->clientMediaType;
210
+ }
211
+ }
src/vendor/guzzlehttp/psr7/src/Uri.php ADDED
@@ -0,0 +1,738 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use GuzzleHttp\Psr7\Exception\MalformedUriException;
8
+ use Psr\Http\Message\UriInterface;
9
+
10
+ /**
11
+ * PSR-7 URI implementation.
12
+ *
13
+ * @author Michael Dowling
14
+ * @author Tobias Schultze
15
+ * @author Matthew Weier O'Phinney
16
+ */
17
+ class Uri implements UriInterface, \JsonSerializable
18
+ {
19
+ /**
20
+ * Absolute http and https URIs require a host per RFC 7230 Section 2.7
21
+ * but in generic URIs the host can be empty. So for http(s) URIs
22
+ * we apply this default host when no host is given yet to form a
23
+ * valid URI.
24
+ */
25
+ private const HTTP_DEFAULT_HOST = 'localhost';
26
+
27
+ private const DEFAULT_PORTS = [
28
+ 'http' => 80,
29
+ 'https' => 443,
30
+ 'ftp' => 21,
31
+ 'gopher' => 70,
32
+ 'nntp' => 119,
33
+ 'news' => 119,
34
+ 'telnet' => 23,
35
+ 'tn3270' => 23,
36
+ 'imap' => 143,
37
+ 'pop' => 110,
38
+ 'ldap' => 389,
39
+ ];
40
+
41
+ /**
42
+ * Unreserved characters for use in a regex.
43
+ *
44
+ * @link https://tools.ietf.org/html/rfc3986#section-2.3
45
+ */
46
+ private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
47
+
48
+ /**
49
+ * Sub-delims for use in a regex.
50
+ *
51
+ * @link https://tools.ietf.org/html/rfc3986#section-2.2
52
+ */
53
+ private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
54
+ private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26'];
55
+
56
+ /** @var string Uri scheme. */
57
+ private $scheme = '';
58
+
59
+ /** @var string Uri user info. */
60
+ private $userInfo = '';
61
+
62
+ /** @var string Uri host. */
63
+ private $host = '';
64
+
65
+ /** @var int|null Uri port. */
66
+ private $port;
67
+
68
+ /** @var string Uri path. */
69
+ private $path = '';
70
+
71
+ /** @var string Uri query string. */
72
+ private $query = '';
73
+
74
+ /** @var string Uri fragment. */
75
+ private $fragment = '';
76
+
77
+ /** @var string|null String representation */
78
+ private $composedComponents;
79
+
80
+ public function __construct(string $uri = '')
81
+ {
82
+ if ($uri !== '') {
83
+ $parts = self::parse($uri);
84
+ if ($parts === false) {
85
+ throw new MalformedUriException("Unable to parse URI: $uri");
86
+ }
87
+ $this->applyParts($parts);
88
+ }
89
+ }
90
+ /**
91
+ * UTF-8 aware \parse_url() replacement.
92
+ *
93
+ * The internal function produces broken output for non ASCII domain names
94
+ * (IDN) when used with locales other than "C".
95
+ *
96
+ * On the other hand, cURL understands IDN correctly only when UTF-8 locale
97
+ * is configured ("C.UTF-8", "en_US.UTF-8", etc.).
98
+ *
99
+ * @see https://bugs.php.net/bug.php?id=52923
100
+ * @see https://www.php.net/manual/en/function.parse-url.php#114817
101
+ * @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING
102
+ *
103
+ * @return array|false
104
+ */
105
+ private static function parse(string $url)
106
+ {
107
+ // If IPv6
108
+ $prefix = '';
109
+ if (preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)) {
110
+ /** @var array{0:string, 1:string, 2:string} $matches */
111
+ $prefix = $matches[1];
112
+ $url = $matches[2];
113
+ }
114
+
115
+ /** @var string */
116
+ $encodedUrl = preg_replace_callback(
117
+ '%[^:/@?&=#]+%usD',
118
+ static function ($matches) {
119
+ return urlencode($matches[0]);
120
+ },
121
+ $url
122
+ );
123
+
124
+ $result = parse_url($prefix . $encodedUrl);
125
+
126
+ if ($result === false) {
127
+ return false;
128
+ }
129
+
130
+ return array_map('urldecode', $result);
131
+ }
132
+
133
+ public function __toString(): string
134
+ {
135
+ if ($this->composedComponents === null) {
136
+ $this->composedComponents = self::composeComponents(
137
+ $this->scheme,
138
+ $this->getAuthority(),
139
+ $this->path,
140
+ $this->query,
141
+ $this->fragment
142
+ );
143
+ }
144
+
145
+ return $this->composedComponents;
146
+ }
147
+
148
+ /**
149
+ * Composes a URI reference string from its various components.
150
+ *
151
+ * Usually this method does not need to be called manually but instead is used indirectly via
152
+ * `Psr\Http\Message\UriInterface::__toString`.
153
+ *
154
+ * PSR-7 UriInterface treats an empty component the same as a missing component as
155
+ * getQuery(), getFragment() etc. always return a string. This explains the slight
156
+ * difference to RFC 3986 Section 5.3.
157
+ *
158
+ * Another adjustment is that the authority separator is added even when the authority is missing/empty
159
+ * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
160
+ * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
161
+ * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
162
+ * that format).
163
+ *
164
+ * @link https://tools.ietf.org/html/rfc3986#section-5.3
165
+ */
166
+ public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string
167
+ {
168
+ $uri = '';
169
+
170
+ // weak type checks to also accept null until we can add scalar type hints
171
+ if ($scheme != '') {
172
+ $uri .= $scheme . ':';
173
+ }
174
+
175
+ if ($authority != ''|| $scheme === 'file') {
176
+ $uri .= '//' . $authority;
177
+ }
178
+
179
+ $uri .= $path;
180
+
181
+ if ($query != '') {
182
+ $uri .= '?' . $query;
183
+ }
184
+
185
+ if ($fragment != '') {
186
+ $uri .= '#' . $fragment;
187
+ }
188
+
189
+ return $uri;
190
+ }
191
+
192
+ /**
193
+ * Whether the URI has the default port of the current scheme.
194
+ *
195
+ * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
196
+ * independently of the implementation.
197
+ */
198
+ public static function isDefaultPort(UriInterface $uri): bool
199
+ {
200
+ return $uri->getPort() === null
201
+ || (isset(self::DEFAULT_PORTS[$uri->getScheme()]) && $uri->getPort() === self::DEFAULT_PORTS[$uri->getScheme()]);
202
+ }
203
+
204
+ /**
205
+ * Whether the URI is absolute, i.e. it has a scheme.
206
+ *
207
+ * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
208
+ * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
209
+ * to another URI, the base URI. Relative references can be divided into several forms:
210
+ * - network-path references, e.g. '//example.com/path'
211
+ * - absolute-path references, e.g. '/path'
212
+ * - relative-path references, e.g. 'subpath'
213
+ *
214
+ * @see Uri::isNetworkPathReference
215
+ * @see Uri::isAbsolutePathReference
216
+ * @see Uri::isRelativePathReference
217
+ * @link https://tools.ietf.org/html/rfc3986#section-4
218
+ */
219
+ public static function isAbsolute(UriInterface $uri): bool
220
+ {
221
+ return $uri->getScheme() !== '';
222
+ }
223
+
224
+ /**
225
+ * Whether the URI is a network-path reference.
226
+ *
227
+ * A relative reference that begins with two slash characters is termed an network-path reference.
228
+ *
229
+ * @link https://tools.ietf.org/html/rfc3986#section-4.2
230
+ */
231
+ public static function isNetworkPathReference(UriInterface $uri): bool
232
+ {
233
+ return $uri->getScheme() === '' && $uri->getAuthority() !== '';
234
+ }
235
+
236
+ /**
237
+ * Whether the URI is a absolute-path reference.
238
+ *
239
+ * A relative reference that begins with a single slash character is termed an absolute-path reference.
240
+ *
241
+ * @link https://tools.ietf.org/html/rfc3986#section-4.2
242
+ */
243
+ public static function isAbsolutePathReference(UriInterface $uri): bool
244
+ {
245
+ return $uri->getScheme() === ''
246
+ && $uri->getAuthority() === ''
247
+ && isset($uri->getPath()[0])
248
+ && $uri->getPath()[0] === '/';
249
+ }
250
+
251
+ /**
252
+ * Whether the URI is a relative-path reference.
253
+ *
254
+ * A relative reference that does not begin with a slash character is termed a relative-path reference.
255
+ *
256
+ * @link https://tools.ietf.org/html/rfc3986#section-4.2
257
+ */
258
+ public static function isRelativePathReference(UriInterface $uri): bool
259
+ {
260
+ return $uri->getScheme() === ''
261
+ && $uri->getAuthority() === ''
262
+ && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
263
+ }
264
+
265
+ /**
266
+ * Whether the URI is a same-document reference.
267
+ *
268
+ * A same-document reference refers to a URI that is, aside from its fragment
269
+ * component, identical to the base URI. When no base URI is given, only an empty
270
+ * URI reference (apart from its fragment) is considered a same-document reference.
271
+ *
272
+ * @param UriInterface $uri The URI to check
273
+ * @param UriInterface|null $base An optional base URI to compare against
274
+ *
275
+ * @link https://tools.ietf.org/html/rfc3986#section-4.4
276
+ */
277
+ public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool
278
+ {
279
+ if ($base !== null) {
280
+ $uri = UriResolver::resolve($base, $uri);
281
+
282
+ return ($uri->getScheme() === $base->getScheme())
283
+ && ($uri->getAuthority() === $base->getAuthority())
284
+ && ($uri->getPath() === $base->getPath())
285
+ && ($uri->getQuery() === $base->getQuery());
286
+ }
287
+
288
+ return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
289
+ }
290
+
291
+ /**
292
+ * Creates a new URI with a specific query string value removed.
293
+ *
294
+ * Any existing query string values that exactly match the provided key are
295
+ * removed.
296
+ *
297
+ * @param UriInterface $uri URI to use as a base.
298
+ * @param string $key Query string key to remove.
299
+ */
300
+ public static function withoutQueryValue(UriInterface $uri, string $key): UriInterface
301
+ {
302
+ $result = self::getFilteredQueryString($uri, [$key]);
303
+
304
+ return $uri->withQuery(implode('&', $result));
305
+ }
306
+
307
+ /**
308
+ * Creates a new URI with a specific query string value.
309
+ *
310
+ * Any existing query string values that exactly match the provided key are
311
+ * removed and replaced with the given key value pair.
312
+ *
313
+ * A value of null will set the query string key without a value, e.g. "key"
314
+ * instead of "key=value".
315
+ *
316
+ * @param UriInterface $uri URI to use as a base.
317
+ * @param string $key Key to set.
318
+ * @param string|null $value Value to set
319
+ */
320
+ public static function withQueryValue(UriInterface $uri, string $key, ?string $value): UriInterface
321
+ {
322
+ $result = self::getFilteredQueryString($uri, [$key]);
323
+
324
+ $result[] = self::generateQueryString($key, $value);
325
+
326
+ return $uri->withQuery(implode('&', $result));
327
+ }
328
+
329
+ /**
330
+ * Creates a new URI with multiple specific query string values.
331
+ *
332
+ * It has the same behavior as withQueryValue() but for an associative array of key => value.
333
+ *
334
+ * @param UriInterface $uri URI to use as a base.
335
+ * @param array<string, string|null> $keyValueArray Associative array of key and values
336
+ */
337
+ public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface
338
+ {
339
+ $result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
340
+
341
+ foreach ($keyValueArray as $key => $value) {
342
+ $result[] = self::generateQueryString((string) $key, $value !== null ? (string) $value : null);
343
+ }
344
+
345
+ return $uri->withQuery(implode('&', $result));
346
+ }
347
+
348
+ /**
349
+ * Creates a URI from a hash of `parse_url` components.
350
+ *
351
+ * @link http://php.net/manual/en/function.parse-url.php
352
+ *
353
+ * @throws MalformedUriException If the components do not form a valid URI.
354
+ */
355
+ public static function fromParts(array $parts): UriInterface
356
+ {
357
+ $uri = new self();
358
+ $uri->applyParts($parts);
359
+ $uri->validateState();
360
+
361
+ return $uri;
362
+ }
363
+
364
+ public function getScheme(): string
365
+ {
366
+ return $this->scheme;
367
+ }
368
+
369
+ public function getAuthority(): string
370
+ {
371
+ $authority = $this->host;
372
+ if ($this->userInfo !== '') {
373
+ $authority = $this->userInfo . '@' . $authority;
374
+ }
375
+
376
+ if ($this->port !== null) {
377
+ $authority .= ':' . $this->port;
378
+ }
379
+
380
+ return $authority;
381
+ }
382
+
383
+ public function getUserInfo(): string
384
+ {
385
+ return $this->userInfo;
386
+ }
387
+
388
+ public function getHost(): string
389
+ {
390
+ return $this->host;
391
+ }
392
+
393
+ public function getPort(): ?int
394
+ {
395
+ return $this->port;
396
+ }
397
+
398
+ public function getPath(): string
399
+ {
400
+ return $this->path;
401
+ }
402
+
403
+ public function getQuery(): string
404
+ {
405
+ return $this->query;
406
+ }
407
+
408
+ public function getFragment(): string
409
+ {
410
+ return $this->fragment;
411
+ }
412
+
413
+ public function withScheme($scheme): UriInterface
414
+ {
415
+ $scheme = $this->filterScheme($scheme);
416
+
417
+ if ($this->scheme === $scheme) {
418
+ return $this;
419
+ }
420
+
421
+ $new = clone $this;
422
+ $new->scheme = $scheme;
423
+ $new->composedComponents = null;
424
+ $new->removeDefaultPort();
425
+ $new->validateState();
426
+
427
+ return $new;
428
+ }
429
+
430
+ public function withUserInfo($user, $password = null): UriInterface
431
+ {
432
+ $info = $this->filterUserInfoComponent($user);
433
+ if ($password !== null) {
434
+ $info .= ':' . $this->filterUserInfoComponent($password);
435
+ }
436
+
437
+ if ($this->userInfo === $info) {
438
+ return $this;
439
+ }
440
+
441
+ $new = clone $this;
442
+ $new->userInfo = $info;
443
+ $new->composedComponents = null;
444
+ $new->validateState();
445
+
446
+ return $new;
447
+ }
448
+
449
+ public function withHost($host): UriInterface
450
+ {
451
+ $host = $this->filterHost($host);
452
+
453
+ if ($this->host === $host) {
454
+ return $this;
455
+ }
456
+
457
+ $new = clone $this;
458
+ $new->host = $host;
459
+ $new->composedComponents = null;
460
+ $new->validateState();
461
+
462
+ return $new;
463
+ }
464
+
465
+ public function withPort($port): UriInterface
466
+ {
467
+ $port = $this->filterPort($port);
468
+
469
+ if ($this->port === $port) {
470
+ return $this;
471
+ }
472
+
473
+ $new = clone $this;
474
+ $new->port = $port;
475
+ $new->composedComponents = null;
476
+ $new->removeDefaultPort();
477
+ $new->validateState();
478
+
479
+ return $new;
480
+ }
481
+
482
+ public function withPath($path): UriInterface
483
+ {
484
+ $path = $this->filterPath($path);
485
+
486
+ if ($this->path === $path) {
487
+ return $this;
488
+ }
489
+
490
+ $new = clone $this;
491
+ $new->path = $path;
492
+ $new->composedComponents = null;
493
+ $new->validateState();
494
+
495
+ return $new;
496
+ }
497
+
498
+ public function withQuery($query): UriInterface
499
+ {
500
+ $query = $this->filterQueryAndFragment($query);
501
+
502
+ if ($this->query === $query) {
503
+ return $this;
504
+ }
505
+
506
+ $new = clone $this;
507
+ $new->query = $query;
508
+ $new->composedComponents = null;
509
+
510
+ return $new;
511
+ }
512
+
513
+ public function withFragment($fragment): UriInterface
514
+ {
515
+ $fragment = $this->filterQueryAndFragment($fragment);
516
+
517
+ if ($this->fragment === $fragment) {
518
+ return $this;
519
+ }
520
+
521
+ $new = clone $this;
522
+ $new->fragment = $fragment;
523
+ $new->composedComponents = null;
524
+
525
+ return $new;
526
+ }
527
+
528
+ public function jsonSerialize(): string
529
+ {
530
+ return $this->__toString();
531
+ }
532
+
533
+ /**
534
+ * Apply parse_url parts to a URI.
535
+ *
536
+ * @param array $parts Array of parse_url parts to apply.
537
+ */
538
+ private function applyParts(array $parts): void
539
+ {
540
+ $this->scheme = isset($parts['scheme'])
541
+ ? $this->filterScheme($parts['scheme'])
542
+ : '';
543
+ $this->userInfo = isset($parts['user'])
544
+ ? $this->filterUserInfoComponent($parts['user'])
545
+ : '';
546
+ $this->host = isset($parts['host'])
547
+ ? $this->filterHost($parts['host'])
548
+ : '';
549
+ $this->port = isset($parts['port'])
550
+ ? $this->filterPort($parts['port'])
551
+ : null;
552
+ $this->path = isset($parts['path'])
553
+ ? $this->filterPath($parts['path'])
554
+ : '';
555
+ $this->query = isset($parts['query'])
556
+ ? $this->filterQueryAndFragment($parts['query'])
557
+ : '';
558
+ $this->fragment = isset($parts['fragment'])
559
+ ? $this->filterQueryAndFragment($parts['fragment'])
560
+ : '';
561
+ if (isset($parts['pass'])) {
562
+ $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']);
563
+ }
564
+
565
+ $this->removeDefaultPort();
566
+ }
567
+
568
+ /**
569
+ * @param mixed $scheme
570
+ *
571
+ * @throws \InvalidArgumentException If the scheme is invalid.
572
+ */
573
+ private function filterScheme($scheme): string
574
+ {
575
+ if (!is_string($scheme)) {
576
+ throw new \InvalidArgumentException('Scheme must be a string');
577
+ }
578
+
579
+ return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
580
+ }
581
+
582
+ /**
583
+ * @param mixed $component
584
+ *
585
+ * @throws \InvalidArgumentException If the user info is invalid.
586
+ */
587
+ private function filterUserInfoComponent($component): string
588
+ {
589
+ if (!is_string($component)) {
590
+ throw new \InvalidArgumentException('User info must be a string');
591
+ }
592
+
593
+ return preg_replace_callback(
594
+ '/(?:[^%' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . ']+|%(?![A-Fa-f0-9]{2}))/',
595
+ [$this, 'rawurlencodeMatchZero'],
596
+ $component
597
+ );
598
+ }
599
+
600
+ /**
601
+ * @param mixed $host
602
+ *
603
+ * @throws \InvalidArgumentException If the host is invalid.
604
+ */
605
+ private function filterHost($host): string
606
+ {
607
+ if (!is_string($host)) {
608
+ throw new \InvalidArgumentException('Host must be a string');
609
+ }
610
+
611
+ return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
612
+ }
613
+
614
+ /**
615
+ * @param mixed $port
616
+ *
617
+ * @throws \InvalidArgumentException If the port is invalid.
618
+ */
619
+ private function filterPort($port): ?int
620
+ {
621
+ if ($port === null) {
622
+ return null;
623
+ }
624
+
625
+ $port = (int) $port;
626
+ if (0 > $port || 0xffff < $port) {
627
+ throw new \InvalidArgumentException(
628
+ sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
629
+ );
630
+ }
631
+
632
+ return $port;
633
+ }
634
+
635
+ /**
636
+ * @param string[] $keys
637
+ *
638
+ * @return string[]
639
+ */
640
+ private static function getFilteredQueryString(UriInterface $uri, array $keys): array
641
+ {
642
+ $current = $uri->getQuery();
643
+
644
+ if ($current === '') {
645
+ return [];
646
+ }
647
+
648
+ $decodedKeys = array_map('rawurldecode', $keys);
649
+
650
+ return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
651
+ return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
652
+ });
653
+ }
654
+
655
+ private static function generateQueryString(string $key, ?string $value): string
656
+ {
657
+ // Query string separators ("=", "&") within the key or value need to be encoded
658
+ // (while preventing double-encoding) before setting the query string. All other
659
+ // chars that need percent-encoding will be encoded by withQuery().
660
+ $queryString = strtr($key, self::QUERY_SEPARATORS_REPLACEMENT);
661
+
662
+ if ($value !== null) {
663
+ $queryString .= '=' . strtr($value, self::QUERY_SEPARATORS_REPLACEMENT);
664
+ }
665
+
666
+ return $queryString;
667
+ }
668
+
669
+ private function removeDefaultPort(): void
670
+ {
671
+ if ($this->port !== null && self::isDefaultPort($this)) {
672
+ $this->port = null;
673
+ }
674
+ }
675
+
676
+ /**
677
+ * Filters the path of a URI
678
+ *
679
+ * @param mixed $path
680
+ *
681
+ * @throws \InvalidArgumentException If the path is invalid.
682
+ */
683
+ private function filterPath($path): string
684
+ {
685
+ if (!is_string($path)) {
686
+ throw new \InvalidArgumentException('Path must be a string');
687
+ }
688
+
689
+ return preg_replace_callback(
690
+ '/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
691
+ [$this, 'rawurlencodeMatchZero'],
692
+ $path
693
+ );
694
+ }
695
+
696
+ /**
697
+ * Filters the query string or fragment of a URI.
698
+ *
699
+ * @param mixed $str
700
+ *
701
+ * @throws \InvalidArgumentException If the query or fragment is invalid.
702
+ */
703
+ private function filterQueryAndFragment($str): string
704
+ {
705
+ if (!is_string($str)) {
706
+ throw new \InvalidArgumentException('Query and fragment must be a string');
707
+ }
708
+
709
+ return preg_replace_callback(
710
+ '/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
711
+ [$this, 'rawurlencodeMatchZero'],
712
+ $str
713
+ );
714
+ }
715
+
716
+ private function rawurlencodeMatchZero(array $match): string
717
+ {
718
+ return rawurlencode($match[0]);
719
+ }
720
+
721
+ private function validateState(): void
722
+ {
723
+ if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
724
+ $this->host = self::HTTP_DEFAULT_HOST;
725
+ }
726
+
727
+ if ($this->getAuthority() === '') {
728
+ if (0 === strpos($this->path, '//')) {
729
+ throw new MalformedUriException('The path of a URI without an authority must not start with two slashes "//"');
730
+ }
731
+ if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
732
+ throw new MalformedUriException('A relative URI must not have a path beginning with a segment containing a colon');
733
+ }
734
+ } elseif (isset($this->path[0]) && $this->path[0] !== '/') {
735
+ throw new MalformedUriException('The path of a URI with an authority must start with a slash "/" or be empty');
736
+ }
737
+ }
738
+ }
src/vendor/guzzlehttp/psr7/src/UriNormalizer.php ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\UriInterface;
8
+
9
+ /**
10
+ * Provides methods to normalize and compare URIs.
11
+ *
12
+ * @author Tobias Schultze
13
+ *
14
+ * @link https://tools.ietf.org/html/rfc3986#section-6
15
+ */
16
+ final class UriNormalizer
17
+ {
18
+ /**
19
+ * Default normalizations which only include the ones that preserve semantics.
20
+ */
21
+ public const PRESERVING_NORMALIZATIONS =
22
+ self::CAPITALIZE_PERCENT_ENCODING |
23
+ self::DECODE_UNRESERVED_CHARACTERS |
24
+ self::CONVERT_EMPTY_PATH |
25
+ self::REMOVE_DEFAULT_HOST |
26
+ self::REMOVE_DEFAULT_PORT |
27
+ self::REMOVE_DOT_SEGMENTS;
28
+
29
+ /**
30
+ * All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
31
+ *
32
+ * Example: http://example.org/a%c2%b1b → http://example.org/a%C2%B1b
33
+ */
34
+ public const CAPITALIZE_PERCENT_ENCODING = 1;
35
+
36
+ /**
37
+ * Decodes percent-encoded octets of unreserved characters.
38
+ *
39
+ * For consistency, percent-encoded octets in the ranges of ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39),
40
+ * hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers and,
41
+ * when found in a URI, should be decoded to their corresponding unreserved characters by URI normalizers.
42
+ *
43
+ * Example: http://example.org/%7Eusern%61me/ → http://example.org/~username/
44
+ */
45
+ public const DECODE_UNRESERVED_CHARACTERS = 2;
46
+
47
+ /**
48
+ * Converts the empty path to "/" for http and https URIs.
49
+ *
50
+ * Example: http://example.org → http://example.org/
51
+ */
52
+ public const CONVERT_EMPTY_PATH = 4;
53
+
54
+ /**
55
+ * Removes the default host of the given URI scheme from the URI.
56
+ *
57
+ * Only the "file" scheme defines the default host "localhost".
58
+ * All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile`
59
+ * are equivalent according to RFC 3986. The first format is not accepted
60
+ * by PHPs stream functions and thus already normalized implicitly to the
61
+ * second format in the Uri class. See `GuzzleHttp\Psr7\Uri::composeComponents`.
62
+ *
63
+ * Example: file://localhost/myfile → file:///myfile
64
+ */
65
+ public const REMOVE_DEFAULT_HOST = 8;
66
+
67
+ /**
68
+ * Removes the default port of the given URI scheme from the URI.
69
+ *
70
+ * Example: http://example.org:80/ → http://example.org/
71
+ */
72
+ public const REMOVE_DEFAULT_PORT = 16;
73
+
74
+ /**
75
+ * Removes unnecessary dot-segments.
76
+ *
77
+ * Dot-segments in relative-path references are not removed as it would
78
+ * change the semantics of the URI reference.
79
+ *
80
+ * Example: http://example.org/../a/b/../c/./d.html → http://example.org/a/c/d.html
81
+ */
82
+ public const REMOVE_DOT_SEGMENTS = 32;
83
+
84
+ /**
85
+ * Paths which include two or more adjacent slashes are converted to one.
86
+ *
87
+ * Webservers usually ignore duplicate slashes and treat those URIs equivalent.
88
+ * But in theory those URIs do not need to be equivalent. So this normalization
89
+ * may change the semantics. Encoded slashes (%2F) are not removed.
90
+ *
91
+ * Example: http://example.org//foo///bar.html → http://example.org/foo/bar.html
92
+ */
93
+ public const REMOVE_DUPLICATE_SLASHES = 64;
94
+
95
+ /**
96
+ * Sort query parameters with their values in alphabetical order.
97
+ *
98
+ * However, the order of parameters in a URI may be significant (this is not defined by the standard).
99
+ * So this normalization is not safe and may change the semantics of the URI.
100
+ *
101
+ * Example: ?lang=en&article=fred → ?article=fred&lang=en
102
+ *
103
+ * Note: The sorting is neither locale nor Unicode aware (the URI query does not get decoded at all) as the
104
+ * purpose is to be able to compare URIs in a reproducible way, not to have the params sorted perfectly.
105
+ */
106
+ public const SORT_QUERY_PARAMETERS = 128;
107
+
108
+ /**
109
+ * Returns a normalized URI.
110
+ *
111
+ * The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
112
+ * This methods adds additional normalizations that can be configured with the $flags parameter.
113
+ *
114
+ * PSR-7 UriInterface cannot distinguish between an empty component and a missing component as
115
+ * getQuery(), getFragment() etc. always return a string. This means the URIs "/?#" and "/" are
116
+ * treated equivalent which is not necessarily true according to RFC 3986. But that difference
117
+ * is highly uncommon in reality. So this potential normalization is implied in PSR-7 as well.
118
+ *
119
+ * @param UriInterface $uri The URI to normalize
120
+ * @param int $flags A bitmask of normalizations to apply, see constants
121
+ *
122
+ * @link https://tools.ietf.org/html/rfc3986#section-6.2
123
+ */
124
+ public static function normalize(UriInterface $uri, int $flags = self::PRESERVING_NORMALIZATIONS): UriInterface
125
+ {
126
+ if ($flags & self::CAPITALIZE_PERCENT_ENCODING) {
127
+ $uri = self::capitalizePercentEncoding($uri);
128
+ }
129
+
130
+ if ($flags & self::DECODE_UNRESERVED_CHARACTERS) {
131
+ $uri = self::decodeUnreservedCharacters($uri);
132
+ }
133
+
134
+ if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === '' &&
135
+ ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
136
+ ) {
137
+ $uri = $uri->withPath('/');
138
+ }
139
+
140
+ if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') {
141
+ $uri = $uri->withHost('');
142
+ }
143
+
144
+ if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) {
145
+ $uri = $uri->withPort(null);
146
+ }
147
+
148
+ if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) {
149
+ $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath()));
150
+ }
151
+
152
+ if ($flags & self::REMOVE_DUPLICATE_SLASHES) {
153
+ $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath()));
154
+ }
155
+
156
+ if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') {
157
+ $queryKeyValues = explode('&', $uri->getQuery());
158
+ sort($queryKeyValues);
159
+ $uri = $uri->withQuery(implode('&', $queryKeyValues));
160
+ }
161
+
162
+ return $uri;
163
+ }
164
+
165
+ /**
166
+ * Whether two URIs can be considered equivalent.
167
+ *
168
+ * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also
169
+ * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be
170
+ * resolved against the same base URI. If this is not the case, determination of equivalence or difference of
171
+ * relative references does not mean anything.
172
+ *
173
+ * @param UriInterface $uri1 An URI to compare
174
+ * @param UriInterface $uri2 An URI to compare
175
+ * @param int $normalizations A bitmask of normalizations to apply, see constants
176
+ *
177
+ * @link https://tools.ietf.org/html/rfc3986#section-6.1
178
+ */
179
+ public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool
180
+ {
181
+ return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations);
182
+ }
183
+
184
+ private static function capitalizePercentEncoding(UriInterface $uri): UriInterface
185
+ {
186
+ $regex = '/(?:%[A-Fa-f0-9]{2})++/';
187
+
188
+ $callback = function (array $match) {
189
+ return strtoupper($match[0]);
190
+ };
191
+
192
+ return
193
+ $uri->withPath(
194
+ preg_replace_callback($regex, $callback, $uri->getPath())
195
+ )->withQuery(
196
+ preg_replace_callback($regex, $callback, $uri->getQuery())
197
+ );
198
+ }
199
+
200
+ private static function decodeUnreservedCharacters(UriInterface $uri): UriInterface
201
+ {
202
+ $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';
203
+
204
+ $callback = function (array $match) {
205
+ return rawurldecode($match[0]);
206
+ };
207
+
208
+ return
209
+ $uri->withPath(
210
+ preg_replace_callback($regex, $callback, $uri->getPath())
211
+ )->withQuery(
212
+ preg_replace_callback($regex, $callback, $uri->getQuery())
213
+ );
214
+ }
215
+
216
+ private function __construct()
217
+ {
218
+ // cannot be instantiated
219
+ }
220
+ }
src/vendor/guzzlehttp/psr7/src/UriResolver.php ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\UriInterface;
8
+
9
+ /**
10
+ * Resolves a URI reference in the context of a base URI and the opposite way.
11
+ *
12
+ * @author Tobias Schultze
13
+ *
14
+ * @link https://tools.ietf.org/html/rfc3986#section-5
15
+ */
16
+ final class UriResolver
17
+ {
18
+ /**
19
+ * Removes dot segments from a path and returns the new path.
20
+ *
21
+ * @link http://tools.ietf.org/html/rfc3986#section-5.2.4
22
+ */
23
+ public static function removeDotSegments(string $path): string
24
+ {
25
+ if ($path === '' || $path === '/') {
26
+ return $path;
27
+ }
28
+
29
+ $results = [];
30
+ $segments = explode('/', $path);
31
+ foreach ($segments as $segment) {
32
+ if ($segment === '..') {
33
+ array_pop($results);
34
+ } elseif ($segment !== '.') {
35
+ $results[] = $segment;
36
+ }
37
+ }
38
+
39
+ $newPath = implode('/', $results);
40
+
41
+ if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) {
42
+ // Re-add the leading slash if necessary for cases like "/.."
43
+ $newPath = '/' . $newPath;
44
+ } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) {
45
+ // Add the trailing slash if necessary
46
+ // If newPath is not empty, then $segment must be set and is the last segment from the foreach
47
+ $newPath .= '/';
48
+ }
49
+
50
+ return $newPath;
51
+ }
52
+
53
+ /**
54
+ * Converts the relative URI into a new URI that is resolved against the base URI.
55
+ *
56
+ * @link http://tools.ietf.org/html/rfc3986#section-5.2
57
+ */
58
+ public static function resolve(UriInterface $base, UriInterface $rel): UriInterface
59
+ {
60
+ if ((string) $rel === '') {
61
+ // we can simply return the same base URI instance for this same-document reference
62
+ return $base;
63
+ }
64
+
65
+ if ($rel->getScheme() != '') {
66
+ return $rel->withPath(self::removeDotSegments($rel->getPath()));
67
+ }
68
+
69
+ if ($rel->getAuthority() != '') {
70
+ $targetAuthority = $rel->getAuthority();
71
+ $targetPath = self::removeDotSegments($rel->getPath());
72
+ $targetQuery = $rel->getQuery();
73
+ } else {
74
+ $targetAuthority = $base->getAuthority();
75
+ if ($rel->getPath() === '') {
76
+ $targetPath = $base->getPath();
77
+ $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery();
78
+ } else {
79
+ if ($rel->getPath()[0] === '/') {
80
+ $targetPath = $rel->getPath();
81
+ } else {
82
+ if ($targetAuthority != '' && $base->getPath() === '') {
83
+ $targetPath = '/' . $rel->getPath();
84
+ } else {
85
+ $lastSlashPos = strrpos($base->getPath(), '/');
86
+ if ($lastSlashPos === false) {
87
+ $targetPath = $rel->getPath();
88
+ } else {
89
+ $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath();
90
+ }
91
+ }
92
+ }
93
+ $targetPath = self::removeDotSegments($targetPath);
94
+ $targetQuery = $rel->getQuery();
95
+ }
96
+ }
97
+
98
+ return new Uri(Uri::composeComponents(
99
+ $base->getScheme(),
100
+ $targetAuthority,
101
+ $targetPath,
102
+ $targetQuery,
103
+ $rel->getFragment()
104
+ ));
105
+ }
106
+
107
+ /**
108
+ * Returns the target URI as a relative reference from the base URI.
109
+ *
110
+ * This method is the counterpart to resolve():
111
+ *
112
+ * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
113
+ *
114
+ * One use-case is to use the current request URI as base URI and then generate relative links in your documents
115
+ * to reduce the document size or offer self-contained downloadable document archives.
116
+ *
117
+ * $base = new Uri('http://example.com/a/b/');
118
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
119
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
120
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
121
+ * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
122
+ *
123
+ * This method also accepts a target that is already relative and will try to relativize it further. Only a
124
+ * relative-path reference will be returned as-is.
125
+ *
126
+ * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well
127
+ */
128
+ public static function relativize(UriInterface $base, UriInterface $target): UriInterface
129
+ {
130
+ if ($target->getScheme() !== '' &&
131
+ ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')
132
+ ) {
133
+ return $target;
134
+ }
135
+
136
+ if (Uri::isRelativePathReference($target)) {
137
+ // As the target is already highly relative we return it as-is. It would be possible to resolve
138
+ // the target with `$target = self::resolve($base, $target);` and then try make it more relative
139
+ // by removing a duplicate query. But let's not do that automatically.
140
+ return $target;
141
+ }
142
+
143
+ if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) {
144
+ return $target->withScheme('');
145
+ }
146
+
147
+ // We must remove the path before removing the authority because if the path starts with two slashes, the URI
148
+ // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also
149
+ // invalid.
150
+ $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost('');
151
+
152
+ if ($base->getPath() !== $target->getPath()) {
153
+ return $emptyPathUri->withPath(self::getRelativePath($base, $target));
154
+ }
155
+
156
+ if ($base->getQuery() === $target->getQuery()) {
157
+ // Only the target fragment is left. And it must be returned even if base and target fragment are the same.
158
+ return $emptyPathUri->withQuery('');
159
+ }
160
+
161
+ // If the base URI has a query but the target has none, we cannot return an empty path reference as it would
162
+ // inherit the base query component when resolving.
163
+ if ($target->getQuery() === '') {
164
+ $segments = explode('/', $target->getPath());
165
+ /** @var string $lastSegment */
166
+ $lastSegment = end($segments);
167
+
168
+ return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment);
169
+ }
170
+
171
+ return $emptyPathUri;
172
+ }
173
+
174
+ private static function getRelativePath(UriInterface $base, UriInterface $target): string
175
+ {
176
+ $sourceSegments = explode('/', $base->getPath());
177
+ $targetSegments = explode('/', $target->getPath());
178
+ array_pop($sourceSegments);
179
+ $targetLastSegment = array_pop($targetSegments);
180
+ foreach ($sourceSegments as $i => $segment) {
181
+ if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) {
182
+ unset($sourceSegments[$i], $targetSegments[$i]);
183
+ } else {
184
+ break;
185
+ }
186
+ }
187
+ $targetSegments[] = $targetLastSegment;
188
+ $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments);
189
+
190
+ // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./".
191
+ // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
192
+ // as the first segment of a relative-path reference, as it would be mistaken for a scheme name.
193
+ if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) {
194
+ $relativePath = "./$relativePath";
195
+ } elseif ('/' === $relativePath[0]) {
196
+ if ($base->getAuthority() != '' && $base->getPath() === '') {
197
+ // In this case an extra slash is added by resolve() automatically. So we must not add one here.
198
+ $relativePath = ".$relativePath";
199
+ } else {
200
+ $relativePath = "./$relativePath";
201
+ }
202
+ }
203
+
204
+ return $relativePath;
205
+ }
206
+
207
+ private function __construct()
208
+ {
209
+ // cannot be instantiated
210
+ }
211
+ }
src/vendor/guzzlehttp/psr7/src/Utils.php ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace GuzzleHttp\Psr7;
6
+
7
+ use Psr\Http\Message\RequestInterface;
8
+ use Psr\Http\Message\ServerRequestInterface;
9
+ use Psr\Http\Message\StreamInterface;
10
+ use Psr\Http\Message\UriInterface;
11
+
12
+ final class Utils
13
+ {
14
+ /**
15
+ * Remove the items given by the keys, case insensitively from the data.
16
+ *
17
+ * @param string[] $keys
18
+ */
19
+ public static function caselessRemove(array $keys, array $data): array
20
+ {
21
+ $result = [];
22
+
23
+ foreach ($keys as &$key) {
24
+ $key = strtolower($key);
25
+ }
26
+
27
+ foreach ($data as $k => $v) {
28
+ if (!is_string($k) || !in_array(strtolower($k), $keys)) {
29
+ $result[$k] = $v;
30
+ }
31
+ }
32
+
33
+ return $result;
34
+ }
35
+
36
+ /**
37
+ * Copy the contents of a stream into another stream until the given number
38
+ * of bytes have been read.
39
+ *
40
+ * @param StreamInterface $source Stream to read from
41
+ * @param StreamInterface $dest Stream to write to
42
+ * @param int $maxLen Maximum number of bytes to read. Pass -1
43
+ * to read the entire stream.
44
+ *
45
+ * @throws \RuntimeException on error.
46
+ */
47
+ public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void
48
+ {
49
+ $bufferSize = 8192;
50
+
51
+ if ($maxLen === -1) {
52
+ while (!$source->eof()) {
53
+ if (!$dest->write($source->read($bufferSize))) {
54
+ break;
55
+ }
56
+ }
57
+ } else {
58
+ $remaining = $maxLen;
59
+ while ($remaining > 0 && !$source->eof()) {
60
+ $buf = $source->read(min($bufferSize, $remaining));
61
+ $len = strlen($buf);
62
+ if (!$len) {
63
+ break;
64
+ }
65
+ $remaining -= $len;
66
+ $dest->write($buf);
67
+ }
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Copy the contents of a stream into a string until the given number of
73
+ * bytes have been read.
74
+ *
75
+ * @param StreamInterface $stream Stream to read
76
+ * @param int $maxLen Maximum number of bytes to read. Pass -1
77
+ * to read the entire stream.
78
+ *
79
+ * @throws \RuntimeException on error.
80
+ */
81
+ public static function copyToString(StreamInterface $stream, int $maxLen = -1): string
82
+ {
83
+ $buffer = '';
84
+
85
+ if ($maxLen === -1) {
86
+ while (!$stream->eof()) {
87
+ $buf = $stream->read(1048576);
88
+ if ($buf === '') {
89
+ break;
90
+ }
91
+ $buffer .= $buf;
92
+ }
93
+ return $buffer;
94
+ }
95
+
96
+ $len = 0;
97
+ while (!$stream->eof() && $len < $maxLen) {
98
+ $buf = $stream->read($maxLen - $len);
99
+ if ($buf === '') {
100
+ break;
101
+ }
102
+ $buffer .= $buf;
103
+ $len = strlen($buffer);
104
+ }
105
+
106
+ return $buffer;
107
+ }
108
+
109
+ /**
110
+ * Calculate a hash of a stream.
111
+ *
112
+ * This method reads the entire stream to calculate a rolling hash, based
113
+ * on PHP's `hash_init` functions.
114
+ *
115
+ * @param StreamInterface $stream Stream to calculate the hash for
116
+ * @param string $algo Hash algorithm (e.g. md5, crc32, etc)
117
+ * @param bool $rawOutput Whether or not to use raw output
118
+ *
119
+ * @throws \RuntimeException on error.
120
+ */
121
+ public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string
122
+ {
123
+ $pos = $stream->tell();
124
+
125
+ if ($pos > 0) {
126
+ $stream->rewind();
127
+ }
128
+
129
+ $ctx = hash_init($algo);
130
+ while (!$stream->eof()) {
131
+ hash_update($ctx, $stream->read(1048576));
132
+ }
133
+
134
+ $out = hash_final($ctx, $rawOutput);
135
+ $stream->seek($pos);
136
+
137
+ return $out;
138
+ }
139
+
140
+ /**
141
+ * Clone and modify a request with the given changes.
142
+ *
143
+ * This method is useful for reducing the number of clones needed to mutate
144
+ * a message.
145
+ *
146
+ * The changes can be one of:
147
+ * - method: (string) Changes the HTTP method.
148
+ * - set_headers: (array) Sets the given headers.
149
+ * - remove_headers: (array) Remove the given headers.
150
+ * - body: (mixed) Sets the given body.
151
+ * - uri: (UriInterface) Set the URI.
152
+ * - query: (string) Set the query string value of the URI.
153
+ * - version: (string) Set the protocol version.
154
+ *
155
+ * @param RequestInterface $request Request to clone and modify.
156
+ * @param array $changes Changes to apply.
157
+ */
158
+ public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface
159
+ {
160
+ if (!$changes) {
161
+ return $request;
162
+ }
163
+
164
+ $headers = $request->getHeaders();
165
+
166
+ if (!isset($changes['uri'])) {
167
+ $uri = $request->getUri();
168
+ } else {
169
+ // Remove the host header if one is on the URI
170
+ if ($host = $changes['uri']->getHost()) {
171
+ $changes['set_headers']['Host'] = $host;
172
+
173
+ if ($port = $changes['uri']->getPort()) {
174
+ $standardPorts = ['http' => 80, 'https' => 443];
175
+ $scheme = $changes['uri']->getScheme();
176
+ if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
177
+ $changes['set_headers']['Host'] .= ':' . $port;
178
+ }
179
+ }
180
+ }
181
+ $uri = $changes['uri'];
182
+ }
183
+
184
+ if (!empty($changes['remove_headers'])) {
185
+ $headers = self::caselessRemove($changes['remove_headers'], $headers);
186
+ }
187
+
188
+ if (!empty($changes['set_headers'])) {
189
+ $headers = self::caselessRemove(array_keys($changes['set_headers']), $headers);
190
+ $headers = $changes['set_headers'] + $headers;
191
+ }
192
+
193
+ if (isset($changes['query'])) {
194
+ $uri = $uri->withQuery($changes['query']);
195
+ }
196
+
197
+ if ($request instanceof ServerRequestInterface) {
198
+ $new = (new ServerRequest(
199
+ $changes['method'] ?? $request->getMethod(),
200
+ $uri,
201
+ $headers,
202
+ $changes['body'] ?? $request->getBody(),
203
+ $changes['version'] ?? $request->getProtocolVersion(),
204
+ $request->getServerParams()
205
+ ))
206
+ ->withParsedBody($request->getParsedBody())
207
+ ->withQueryParams($request->getQueryParams())
208
+ ->withCookieParams($request->getCookieParams())
209
+ ->withUploadedFiles($request->getUploadedFiles());
210
+
211
+ foreach ($request->getAttributes() as $key => $value) {
212
+ $new = $new->withAttribute($key, $value);
213
+ }
214
+
215
+ return $new;
216
+ }
217
+
218
+ return new Request(
219
+ $changes['method'] ?? $request->getMethod(),
220
+ $uri,
221
+ $headers,
222
+ $changes['body'] ?? $request->getBody(),
223
+ $changes['version'] ?? $request->getProtocolVersion()
224
+ );
225
+ }
226
+
227
+ /**
228
+ * Read a line from the stream up to the maximum allowed buffer length.
229
+ *
230
+ * @param StreamInterface $stream Stream to read from
231
+ * @param int|null $maxLength Maximum buffer length
232
+ */
233
+ public static function readLine(StreamInterface $stream, ?int $maxLength = null): string
234
+ {
235
+ $buffer = '';
236
+ $size = 0;
237
+
238
+ while (!$stream->eof()) {
239
+ if ('' === ($byte = $stream->read(1))) {
240
+ return $buffer;
241
+ }
242
+ $buffer .= $byte;
243
+ // Break when a new line is found or the max length - 1 is reached
244
+ if ($byte === "\n" || ++$size === $maxLength - 1) {
245
+ break;
246
+ }
247
+ }
248
+
249
+ return $buffer;
250
+ }
251
+
252
+ /**
253
+ * Create a new stream based on the input type.
254
+ *
255
+ * Options is an associative array that can contain the following keys:
256
+ * - metadata: Array of custom metadata.
257
+ * - size: Size of the stream.
258
+ *
259
+ * This method accepts the following `$resource` types:
260
+ * - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
261
+ * - `string`: Creates a stream object that uses the given string as the contents.
262
+ * - `resource`: Creates a stream object that wraps the given PHP stream resource.
263
+ * - `Iterator`: If the provided value implements `Iterator`, then a read-only
264
+ * stream object will be created that wraps the given iterable. Each time the
265
+ * stream is read from, data from the iterator will fill a buffer and will be
266
+ * continuously called until the buffer is equal to the requested read size.
267
+ * Subsequent read calls will first read from the buffer and then call `next`
268
+ * on the underlying iterator until it is exhausted.
269
+ * - `object` with `__toString()`: If the object has the `__toString()` method,
270
+ * the object will be cast to a string and then a stream will be returned that
271
+ * uses the string value.
272
+ * - `NULL`: When `null` is passed, an empty stream object is returned.
273
+ * - `callable` When a callable is passed, a read-only stream object will be
274
+ * created that invokes the given callable. The callable is invoked with the
275
+ * number of suggested bytes to read. The callable can return any number of
276
+ * bytes, but MUST return `false` when there is no more data to return. The
277
+ * stream object that wraps the callable will invoke the callable until the
278
+ * number of requested bytes are available. Any additional bytes will be
279
+ * buffered and used in subsequent reads.
280
+ *
281
+ * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
282
+ * @param array{size?: int, metadata?: array} $options Additional options
283
+ *
284
+ * @throws \InvalidArgumentException if the $resource arg is not valid.
285
+ */
286
+ public static function streamFor($resource = '', array $options = []): StreamInterface
287
+ {
288
+ if (is_scalar($resource)) {
289
+ $stream = self::tryFopen('php://temp', 'r+');
290
+ if ($resource !== '') {
291
+ fwrite($stream, (string) $resource);
292
+ fseek($stream, 0);
293
+ }
294
+ return new Stream($stream, $options);
295
+ }
296
+
297
+ switch (gettype($resource)) {
298
+ case 'resource':
299
+ /*
300
+ * The 'php://input' is a special stream with quirks and inconsistencies.
301
+ * We avoid using that stream by reading it into php://temp
302
+ */
303
+
304
+ /** @var resource $resource */
305
+ if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') {
306
+ $stream = self::tryFopen('php://temp', 'w+');
307
+ stream_copy_to_stream($resource, $stream);
308
+ fseek($stream, 0);
309
+ $resource = $stream;
310
+ }
311
+ return new Stream($resource, $options);
312
+ case 'object':
313
+ /** @var object $resource */
314
+ if ($resource instanceof StreamInterface) {
315
+ return $resource;
316
+ } elseif ($resource instanceof \Iterator) {
317
+ return new PumpStream(function () use ($resource) {
318
+ if (!$resource->valid()) {
319
+ return false;
320
+ }
321
+ $result = $resource->current();
322
+ $resource->next();
323
+ return $result;
324
+ }, $options);
325
+ } elseif (method_exists($resource, '__toString')) {
326
+ return self::streamFor((string) $resource, $options);
327
+ }
328
+ break;
329
+ case 'NULL':
330
+ return new Stream(self::tryFopen('php://temp', 'r+'), $options);
331
+ }
332
+
333
+ if (is_callable($resource)) {
334
+ return new PumpStream($resource, $options);
335
+ }
336
+
337
+ throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
338
+ }
339
+
340
+ /**
341
+ * Safely opens a PHP stream resource using a filename.
342
+ *
343
+ * When fopen fails, PHP normally raises a warning. This function adds an
344
+ * error handler that checks for errors and throws an exception instead.
345
+ *
346
+ * @param string $filename File to open
347
+ * @param string $mode Mode used to open the file
348
+ *
349
+ * @return resource
350
+ *
351
+ * @throws \RuntimeException if the file cannot be opened
352
+ */
353
+ public static function tryFopen(string $filename, string $mode)
354
+ {
355
+ $ex = null;
356
+ set_error_handler(static function (int $errno, string $errstr) use ($filename, $mode, &$ex): bool {
357
+ $ex = new \RuntimeException(sprintf(
358
+ 'Unable to open "%s" using mode "%s": %s',
359
+ $filename,
360
+ $mode,
361
+ $errstr
362
+ ));
363
+
364
+ return true;
365
+ });
366
+
367
+ try {
368
+ /** @var resource $handle */
369
+ $handle = fopen($filename, $mode);
370
+ } catch (\Throwable $e) {
371
+ $ex = new \RuntimeException(sprintf(
372
+ 'Unable to open "%s" using mode "%s": %s',
373
+ $filename,
374
+ $mode,
375
+ $e->getMessage()
376
+ ), 0, $e);
377
+ }
378
+
379
+ restore_error_handler();
380
+
381
+ if ($ex) {
382
+ /** @var $ex \RuntimeException */
383
+ throw $ex;
384
+ }
385
+
386
+ return $handle;
387
+ }
388
+
389
+ /**
390
+ * Safely gets the contents of a given stream.
391
+ *
392
+ * When stream_get_contents fails, PHP normally raises a warning. This
393
+ * function adds an error handler that checks for errors and throws an
394
+ * exception instead.
395
+ *
396
+ * @param resource $stream
397
+ *
398
+ * @throws \RuntimeException if the stream cannot be read
399
+ */
400
+ public static function tryGetContents($stream): string
401
+ {
402
+ $ex = null;
403
+ set_error_handler(static function (int $errno, string $errstr) use (&$ex): bool {
404
+ $ex = new \RuntimeException(sprintf(
405
+ 'Unable to read stream contents: %s',
406
+ $errstr
407
+ ));
408
+
409
+ return true;
410
+ });
411
+
412
+ try {
413
+ /** @var string|false $contents */
414
+ $contents = stream_get_contents($stream);
415
+
416
+ if ($contents === false) {
417
+ $ex = new \RuntimeException('Unable to read stream contents');
418
+ }
419
+ } catch (\Throwable $e) {
420
+ $ex = new \RuntimeException(sprintf(
421
+ 'Unable to read stream contents: %s',
422
+ $e->getMessage()
423
+ ), 0, $e);
424
+ }
425
+
426
+ restore_error_handler();
427
+
428
+ if ($ex) {
429
+ /** @var $ex \RuntimeException */
430
+ throw $ex;
431
+ }
432
+
433
+ return $contents;
434
+ }
435
+
436
+ /**
437
+ * Returns a UriInterface for the given value.
438
+ *
439
+ * This function accepts a string or UriInterface and returns a
440
+ * UriInterface for the given value. If the value is already a
441
+ * UriInterface, it is returned as-is.
442
+ *
443
+ * @param string|UriInterface $uri
444
+ *
445
+ * @throws \InvalidArgumentException
446
+ */
447
+ public static function uriFor($uri): UriInterface
448
+ {
449
+ if ($uri instanceof UriInterface) {
450
+ return $uri;
451
+ }
452
+
453
+ if (is_string($uri)) {
454
+ return new Uri($uri);
455
+ }
456
+
457
+ throw new \InvalidArgumentException('URI must be a string or UriInterface');
458
+ }
459
+ }
src/vendor/http-interop/http-factory-guzzle/.github/workflows/ci.yaml ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [ master ]
7
+
8
+ jobs:
9
+ run:
10
+ runs-on: ubuntu-18.04
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ php:
15
+ - '7.3'
16
+ - '7.4'
17
+ - '8.0'
18
+ minimum_versions: [false]
19
+ coverage: ['none']
20
+ include:
21
+ - description: 'Minimum version'
22
+ php: '7.3'
23
+ minimum_versions: true
24
+ - description: 'Log Code Coverage'
25
+ php: '8.0'
26
+ coverage: 'xdebug'
27
+
28
+ name: PHP ${{ matrix.php }} ${{ matrix.description }}
29
+ steps:
30
+ - name: Checkout
31
+ uses: actions/checkout@v2
32
+
33
+ - uses: actions/cache@v2
34
+ with:
35
+ path: ~/.composer/cache/files
36
+ key: ${{ matrix.php }}
37
+
38
+ - name: Setup PHP
39
+ uses: shivammathur/setup-php@v2
40
+ with:
41
+ php-version: ${{ matrix.php }}
42
+ coverage: ${{ matrix.coverage }}
43
+
44
+ - name: Install dependencies
45
+ run: composer install
46
+ if: matrix.minimum_versions == false
47
+
48
+ - name: Install dependencies (lowest versions)
49
+ run: composer update --no-interaction --prefer-lowest
50
+ if: matrix.minimum_versions == true
51
+
52
+ - name: Run PHPUnit tests
53
+ run: vendor/bin/phpunit
54
+
55
+ - name: Upload code coverage
56
+ uses: codecov/codecov-action@v2
57
+ if: matrix.coverage == 'xdebug'
58
+ with:
59
+ file: './build/logs/clover.xml'
60
+ fail_ci_if_error: true
src/vendor/http-interop/http-factory-guzzle/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Woody Gilk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
src/vendor/http-interop/http-factory-guzzle/README.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ # HTTP Factory for Guzzle
2
+
3
+ HTTP factory implemented for [Guzzle](https://github.com/guzzle/psr7).
4
+
5
+ **NOTE:** `guzzlehttp/psr7` includes an HTTP factory implementation starting with
6
+ version 2.0. Please use the official factory if your project can use
7
+ `"guzzlehttp/psr7": "^2.0"`.
src/vendor/http-interop/http-factory-guzzle/composer.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "http-interop/http-factory-guzzle",
3
+ "description": "An HTTP Factory using Guzzle PSR7",
4
+ "keywords": [
5
+ "psr-7",
6
+ "psr-17",
7
+ "http",
8
+ "factory"
9
+ ],
10
+ "license": "MIT",
11
+ "authors": [
12
+ {
13
+ "name": "PHP-FIG",
14
+ "homepage": "http://www.php-fig.org/"
15
+ }
16
+ ],
17
+ "provide": {
18
+ "psr/http-factory-implementation": "^1.0"
19
+ },
20
+ "require": {
21
+ "php": ">=7.3",
22
+ "psr/http-factory": "^1.0",
23
+ "guzzlehttp/psr7": "^1.7||^2.0"
24
+ },
25
+ "require-dev": {
26
+ "http-interop/http-factory-tests": "^0.9",
27
+ "phpunit/phpunit": "^9.5"
28
+ },
29
+ "autoload": {
30
+ "psr-4": {
31
+ "Http\\Factory\\Guzzle\\": "src/"
32
+ }
33
+ },
34
+ "suggest": {
35
+ "guzzlehttp/psr7": "Includes an HTTP factory starting in version 2.0"
36
+ }
37
+ }
src/vendor/http-interop/http-factory-guzzle/src/RequestFactory.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Factory\Guzzle;
4
+
5
+ use GuzzleHttp\Psr7\Request;
6
+ use Psr\Http\Message\RequestFactoryInterface;
7
+ use Psr\Http\Message\RequestInterface;
8
+
9
+ class RequestFactory implements RequestFactoryInterface
10
+ {
11
+ public function createRequest(string $method, $uri): RequestInterface
12
+ {
13
+ return new Request($method, $uri);
14
+ }
15
+ }
src/vendor/http-interop/http-factory-guzzle/src/ResponseFactory.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Factory\Guzzle;
4
+
5
+ use GuzzleHttp\Psr7\Response;
6
+ use Psr\Http\Message\ResponseFactoryInterface;
7
+ use Psr\Http\Message\ResponseInterface;
8
+
9
+ class ResponseFactory implements ResponseFactoryInterface
10
+ {
11
+ public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
12
+ {
13
+ return new Response($code, [], null, '1.1', $reasonPhrase);
14
+ }
15
+ }
src/vendor/http-interop/http-factory-guzzle/src/ServerRequestFactory.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Factory\Guzzle;
4
+
5
+ use GuzzleHttp\Psr7\ServerRequest;
6
+ use Psr\Http\Message\ServerRequestFactoryInterface;
7
+ use Psr\Http\Message\ServerRequestInterface;
8
+
9
+ class ServerRequestFactory implements ServerRequestFactoryInterface
10
+ {
11
+ public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
12
+ {
13
+
14
+ if (empty($method)) {
15
+ if (!empty($serverParams['REQUEST_METHOD'])) {
16
+ $method = $serverParams['REQUEST_METHOD'];
17
+ } else {
18
+ throw new \InvalidArgumentException('Cannot determine HTTP method');
19
+ }
20
+ }
21
+
22
+ return new ServerRequest($method, $uri, [], null, '1.1', $serverParams);
23
+ }
24
+ }
src/vendor/http-interop/http-factory-guzzle/src/StreamFactory.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Factory\Guzzle;
4
+
5
+ use GuzzleHttp\Psr7\Stream;
6
+ use GuzzleHttp\Psr7\Utils;
7
+ use Psr\Http\Message\StreamFactoryInterface;
8
+ use Psr\Http\Message\StreamInterface;
9
+
10
+ class StreamFactory implements StreamFactoryInterface
11
+ {
12
+ public function createStream(string $content = ''): StreamInterface
13
+ {
14
+ return Utils::streamFor($content);
15
+ }
16
+
17
+ public function createStreamFromFile(string $file, string $mode = 'r'): StreamInterface
18
+ {
19
+ return $this->createStreamFromResource(Utils::tryFopen($file, $mode));
20
+ }
21
+
22
+ public function createStreamFromResource($resource): StreamInterface
23
+ {
24
+ return new Stream($resource);
25
+ }
26
+ }
src/vendor/http-interop/http-factory-guzzle/src/UploadedFileFactory.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Factory\Guzzle;
4
+
5
+ use GuzzleHttp\Psr7\UploadedFile;
6
+ use Psr\Http\Message\UploadedFileFactoryInterface;
7
+ use Psr\Http\Message\StreamInterface;
8
+ use Psr\Http\Message\UploadedFileInterface;
9
+
10
+ class UploadedFileFactory implements UploadedFileFactoryInterface
11
+ {
12
+ public function createUploadedFile(
13
+ StreamInterface $stream,
14
+ int $size = null,
15
+ int $error = \UPLOAD_ERR_OK,
16
+ string $clientFilename = null,
17
+ string $clientMediaType = null
18
+ ): UploadedFileInterface {
19
+ if ($size === null) {
20
+ $size = $stream->getSize();
21
+ }
22
+
23
+ return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
24
+ }
25
+ }
src/vendor/http-interop/http-factory-guzzle/src/UriFactory.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Factory\Guzzle;
4
+
5
+ use GuzzleHttp\Psr7\Uri;
6
+ use Psr\Http\Message\UriFactoryInterface;
7
+ use Psr\Http\Message\UriInterface;
8
+
9
+ class UriFactory implements UriFactoryInterface
10
+ {
11
+ public function createUri(string $uri = ''): UriInterface
12
+ {
13
+ return new Uri($uri);
14
+ }
15
+ }
src/vendor/jean85/pretty-package-versions/.github/workflows/tests.yaml ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Tests
2
+
3
+ on:
4
+ pull_request: null
5
+ push:
6
+ branches:
7
+ - 2.x
8
+
9
+ jobs:
10
+ tests:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ php:
15
+ - '7.1'
16
+ - '7.2'
17
+ - '7.3'
18
+ - '7.4'
19
+ - '8.0'
20
+ - '8.1'
21
+ composer_version: ['v2']
22
+ include:
23
+ - description: '(prefer lowest)'
24
+ php: '7.1'
25
+ composer_version: '2.0.0'
26
+ dependencies: 'lowest'
27
+
28
+ name: PHP ${{ matrix.php }} tests ${{ matrix.description }}
29
+ steps:
30
+ # checkout git
31
+ - uses: actions/checkout@v2
32
+ # setup PHP
33
+ - uses: shivammathur/setup-php@v2
34
+ with:
35
+ php-version: ${{ matrix.php }}
36
+ tools: composer:${{ matrix.composer_version }}
37
+ coverage: xdebug
38
+ - uses: "ramsey/composer-install@v1"
39
+ with:
40
+ dependency-versions: ${{ matrix.dependencies }}
41
+ - run: vendor/bin/phpunit --coverage-clover=coverage.xml
42
+ - uses: codecov/codecov-action@v1
43
+ with:
44
+ file: './coverage.xml'
45
+ fail_ci_if_error: true
46
+ PHP-CS-Fixer:
47
+ runs-on: ubuntu-latest
48
+ name: Code style
49
+ steps:
50
+ - uses: actions/checkout@v2
51
+ - uses: shivammathur/setup-php@v2
52
+ with:
53
+ php-version: '7.4'
54
+ coverage: none
55
+ - uses: "ramsey/composer-install@v1"
56
+ - run: vendor/bin/php-cs-fixer fix --ansi --verbose --dry-run
57
+ PHPStan:
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - uses: actions/checkout@v2
61
+ - uses: shivammathur/setup-php@v2
62
+ with:
63
+ php-version: '7.4'
64
+ coverage: none
65
+ - uses: "ramsey/composer-install@v1"
66
+ - run: vendor/bin/phpstan analyse
67
+ Psalm:
68
+ runs-on: ubuntu-latest
69
+ steps:
70
+ - uses: actions/checkout@v2
71
+ - uses: shivammathur/setup-php@v2
72
+ with:
73
+ php-version: '7.4'
74
+ coverage: none
75
+ - uses: "ramsey/composer-install@v1"
76
+ - run: vendor/bin/psalm
src/vendor/jean85/pretty-package-versions/.php_cs.dist ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $config = new PhpCsFixer\Config('default');
4
+ $config->setRiskyAllowed(true);
5
+ $config->setRules([
6
+ '@PSR2' => true,
7
+ 'align_multiline_comment' => true,
8
+ 'array_indentation' => true,
9
+ 'array_syntax' => [
10
+ 'syntax' => 'short',
11
+ ],
12
+ 'binary_operator_spaces' => [
13
+ 'align_double_arrow' => false,
14
+ 'align_equals' => false,
15
+ ],
16
+ 'blank_line_after_namespace' => true,
17
+ 'blank_line_after_opening_tag' => true,
18
+ 'blank_line_before_statement' => [
19
+ 'statements' => [
20
+ 'return',
21
+ ],
22
+ ],
23
+ 'cast_spaces' => [
24
+ 'space' => 'single',
25
+ ],
26
+ 'class_attributes_separation' => [
27
+ 'elements' => [
28
+ 'method' => 'one',
29
+ 'property' => 'one',
30
+ ],
31
+ ],
32
+ 'compact_nullable_typehint' => true,
33
+ 'concat_space' => [
34
+ 'spacing' => 'one',
35
+ ],
36
+ 'declare_equal_normalize' => true,
37
+ 'declare_strict_types' => true,
38
+ 'function_typehint_space' => true,
39
+ 'include' => true,
40
+ 'indentation_type' => true,
41
+ 'lowercase_cast' => true,
42
+ 'method_chaining_indentation' => true,
43
+ 'method_separation' => true,
44
+ 'multiline_comment_opening_closing' => true,
45
+ 'native_function_casing' => true,
46
+ 'new_with_braces' => true,
47
+ 'no_blank_lines_after_phpdoc' => true,
48
+ 'no_empty_comment' => true,
49
+ 'no_empty_phpdoc' => true,
50
+ 'no_empty_statement' => true,
51
+ 'no_extra_consecutive_blank_lines' => [
52
+ 'tokens' => [
53
+ 'curly_brace_block',
54
+ 'extra',
55
+ 'parenthesis_brace_block',
56
+ 'square_brace_block',
57
+ 'throw',
58
+ 'use',
59
+ ],
60
+ ],
61
+ 'no_leading_import_slash' => true,
62
+ 'no_leading_namespace_whitespace' => true,
63
+ 'no_mixed_echo_print' => [
64
+ 'use' => 'echo',
65
+ ],
66
+ 'no_multiline_whitespace_around_double_arrow' => true,
67
+ 'no_short_bool_cast' => true,
68
+ 'no_singleline_whitespace_before_semicolons' => true,
69
+ 'no_spaces_around_offset' => true,
70
+ 'no_superfluous_phpdoc_tags' => [
71
+ 'allow_mixed' => true,
72
+ ],
73
+ 'no_trailing_comma_in_list_call' => true,
74
+ 'no_trailing_comma_in_singleline_array' => true,
75
+ 'no_unneeded_control_parentheses' => true,
76
+ 'no_unused_imports' => true,
77
+ 'no_whitespace_before_comma_in_array' => true,
78
+ 'no_whitespace_in_blank_line' => true,
79
+ 'not_operator_with_successor_space' => true,
80
+ 'normalize_index_brace' => true,
81
+ 'object_operator_without_whitespace' => true,
82
+ 'ordered_imports' => true,
83
+ 'phpdoc_align' => false,
84
+ 'phpdoc_annotation_without_dot' => true,
85
+ 'phpdoc_indent' => true,
86
+ 'phpdoc_no_package' => true,
87
+ 'phpdoc_no_useless_inheritdoc' => true,
88
+ 'phpdoc_order' => true,
89
+ 'phpdoc_scalar' => true,
90
+ 'phpdoc_separation' => true,
91
+ 'phpdoc_single_line_var_spacing' => true,
92
+ 'phpdoc_to_comment' => true,
93
+ 'phpdoc_trim' => true,
94
+ 'phpdoc_trim_consecutive_blank_line_separation' => true,
95
+ 'phpdoc_types' => true,
96
+ 'phpdoc_var_without_name' => true,
97
+ 'pre_increment' => true,
98
+ 'psr4' => true,
99
+ 'return_type_declaration' => true,
100
+ 'short_scalar_cast' => true,
101
+ 'single_blank_line_before_namespace' => true,
102
+ 'single_class_element_per_statement' => true,
103
+ 'single_quote' => true,
104
+ 'space_after_semicolon' => true,
105
+ 'standardize_not_equals' => true,
106
+ 'ternary_operator_spaces' => true,
107
+ 'ternary_to_null_coalescing' => true,
108
+ 'trailing_comma_in_multiline_array' => true,
109
+ 'trim_array_spaces' => true,
110
+ 'unary_operator_spaces' => true,
111
+ 'whitespace_after_comma_in_array' => true,
112
+ ]);
113
+
114
+ $finder = PhpCsFixer\Finder::create();
115
+ $finder->in(__DIR__);
116
+ $config->setFinder($finder);
117
+
118
+ return $config;
src/vendor/jean85/pretty-package-versions/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Alessandro Lai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
src/vendor/jean85/pretty-package-versions/Makefile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pre-commit-check: composer cs phpstan psalm test
2
+
3
+ composer:
4
+ composer install
5
+
6
+ cs:
7
+ vendor/bin/php-cs-fixer fix --verbose
8
+
9
+ cs-dry-run:
10
+ vendor/bin/php-cs-fixer fix --verbose --dry-run
11
+
12
+ phpstan:
13
+ vendor/bin/phpstan analyze
14
+
15
+ psalm:
16
+ vendor/bin/psalm
17
+
18
+ test:
19
+ vendor/bin/phpunit
src/vendor/jean85/pretty-package-versions/codecov.yml ADDED
@@ -0,0 +1 @@
 
1
+ comment: false
src/vendor/jean85/pretty-package-versions/composer.json ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "jean85/pretty-package-versions",
3
+ "description": "A library to get pretty versions strings of installed dependencies",
4
+ "type": "library",
5
+ "require": {
6
+ "php": "^7.1|^8.0",
7
+ "composer-runtime-api": "^2.0.0"
8
+ },
9
+ "require-dev": {
10
+ "friendsofphp/php-cs-fixer": "^2.17",
11
+ "jean85/composer-provided-replaced-stub-package": "^1.0",
12
+ "phpstan/phpstan": "^0.12.66",
13
+ "phpunit/phpunit": "^7.5|^8.5|^9.4",
14
+ "vimeo/psalm": "^4.3"
15
+ },
16
+ "license": "MIT",
17
+ "authors": [
18
+ {
19
+ "name": "Alessandro Lai",
20
+ "email": "alessandro.lai85@gmail.com"
21
+ }
22
+ ],
23
+ "support": {
24
+ "issues": "https://github.com/Jean85/pretty-package-versions/issues"
25
+ },
26
+ "keywords": [
27
+ "package",
28
+ "versions",
29
+ "composer",
30
+ "release"
31
+ ],
32
+ "config": {
33
+ "sort-packages": true
34
+ },
35
+ "extra": {
36
+ "branch-alias": {
37
+ "dev-master": "1.x-dev"
38
+ }
39
+ },
40
+ "autoload": {
41
+ "psr-4": {
42
+ "Jean85\\": "src/"
43
+ }
44
+ },
45
+ "autoload-dev": {
46
+ "psr-4": {
47
+ "Tests\\": "tests"
48
+ }
49
+ }
50
+ }
src/vendor/jean85/pretty-package-versions/phpstan.neon ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ parameters:
2
+ level: 8
3
+ paths:
4
+ - src/
5
+ - tests/
src/vendor/jean85/pretty-package-versions/psalm.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <psalm
3
+ errorLevel="4"
4
+ resolveFromConfigFile="true"
5
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6
+ xmlns="https://getpsalm.org/schema/config"
7
+ xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
8
+ >
9
+ <projectFiles>
10
+ <directory name="src" />
11
+ <directory name="tests" />
12
+ <ignoreFiles>
13
+ <directory name="vendor" />
14
+ </ignoreFiles>
15
+ </projectFiles>
16
+ </psalm>
src/vendor/jean85/pretty-package-versions/src/Exception/ProvidedPackageException.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Jean85\Exception;
6
+
7
+ class ProvidedPackageException extends \Exception implements VersionMissingExceptionInterface
8
+ {
9
+ public static function create(string $packageName): VersionMissingExceptionInterface
10
+ {
11
+ return new self('Cannot retrieve a version for package ' . $packageName . ' since it is provided, probably a metapackage');
12
+ }
13
+ }
src/vendor/jean85/pretty-package-versions/src/Exception/ReplacedPackageException.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Jean85\Exception;
6
+
7
+ class ReplacedPackageException extends \Exception implements VersionMissingExceptionInterface
8
+ {
9
+ public static function create(string $packageName): VersionMissingExceptionInterface
10
+ {
11
+ return new self('Cannot retrieve a version for package ' . $packageName . ' since it is replaced by some other package');
12
+ }
13
+ }
src/vendor/jean85/pretty-package-versions/src/Exception/VersionMissingExceptionInterface.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Jean85\Exception;
6
+
7
+ interface VersionMissingExceptionInterface extends \Throwable
8
+ {
9
+ public static function create(string $packageName): self;
10
+ }
src/vendor/jean85/pretty-package-versions/src/PrettyVersions.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Jean85;
6
+
7
+ use Composer\InstalledVersions;
8
+ use Jean85\Exception\ProvidedPackageException;
9
+ use Jean85\Exception\ReplacedPackageException;
10
+ use Jean85\Exception\VersionMissingExceptionInterface;
11
+
12
+ class PrettyVersions
13
+ {
14
+ /**
15
+ * @throws VersionMissingExceptionInterface When a package is provided ({@see ProvidedPackageException}) or replaced ({@see ReplacedPackageException})
16
+ */
17
+ public static function getVersion(string $packageName): Version
18
+ {
19
+ self::checkProvidedPackages($packageName);
20
+
21
+ self::checkReplacedPackages($packageName);
22
+
23
+ return new Version(
24
+ $packageName,
25
+ InstalledVersions::getPrettyVersion($packageName),
26
+ InstalledVersions::getReference($packageName)
27
+ );
28
+ }
29
+
30
+ public static function getRootPackageName(): string
31
+ {
32
+ return InstalledVersions::getRootPackage()['name'];
33
+ }
34
+
35
+ public static function getRootPackageVersion(): Version
36
+ {
37
+ return new Version(
38
+ self::getRootPackageName(),
39
+ InstalledVersions::getRootPackage()['pretty_version'],
40
+ InstalledVersions::getRootPackage()['reference']
41
+ );
42
+ }
43
+
44
+ protected static function checkProvidedPackages(string $packageName): void
45
+ {
46
+ if (! method_exists(InstalledVersions::class, 'getAllRawData')) {
47
+ if (isset(InstalledVersions::getRawData()['versions'][$packageName]['provided'])) {
48
+ throw ProvidedPackageException::create($packageName);
49
+ }
50
+
51
+ return;
52
+ }
53
+
54
+ foreach (InstalledVersions::getAllRawData() as $installed) {
55
+ if (isset($installed['versions'][$packageName]['provided'])) {
56
+ throw ProvidedPackageException::create($packageName);
57
+ }
58
+ }
59
+ }
60
+
61
+ protected static function checkReplacedPackages(string $packageName): void
62
+ {
63
+ if (! method_exists(InstalledVersions::class, 'getAllRawData')) {
64
+ if (isset(InstalledVersions::getRawData()['versions'][$packageName]['replaced'])) {
65
+ throw ReplacedPackageException::create($packageName);
66
+ }
67
+
68
+ return;
69
+ }
70
+
71
+ foreach (InstalledVersions::getAllRawData() as $installed) {
72
+ if (isset($installed['versions'][$packageName]['replaced'])) {
73
+ throw ReplacedPackageException::create($packageName);
74
+ }
75
+ }
76
+ }
77
+ }
src/vendor/jean85/pretty-package-versions/src/Version.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Jean85;
6
+
7
+ class Version
8
+ {
9
+ private const SHORT_COMMIT_LENGTH = 7;
10
+
11
+ /** @var string */
12
+ private $packageName;
13
+
14
+ /** @var string */
15
+ private $prettyVersion;
16
+
17
+ /** @var string */
18
+ private $reference;
19
+
20
+ /** @var bool */
21
+ private $versionIsTagged;
22
+
23
+ public const NO_VERSION_TEXT = '{no version}';
24
+ public const NO_REFERENCE_TEXT = '{no reference}';
25
+
26
+ public function __construct(string $packageName, ?string $prettyVersion = null, ?string $reference = null)
27
+ {
28
+ $this->packageName = $packageName;
29
+ $this->prettyVersion = $prettyVersion ?? self::NO_VERSION_TEXT;
30
+ $this->reference = $reference ?? self::NO_REFERENCE_TEXT;
31
+ $this->versionIsTagged = preg_match('/[^v\d.]/', $this->getShortVersion()) === 0;
32
+ }
33
+
34
+ public function getPrettyVersion(): string
35
+ {
36
+ if ($this->versionIsTagged) {
37
+ return $this->prettyVersion;
38
+ }
39
+
40
+ return $this->getVersionWithShortReference();
41
+ }
42
+
43
+ public function getFullVersion(): string
44
+ {
45
+ return $this->prettyVersion . '@' . $this->getReference();
46
+ }
47
+
48
+ /**
49
+ * @deprecated
50
+ */
51
+ public function getVersionWithShortCommit(): string
52
+ {
53
+ return $this->getVersionWithShortReference();
54
+ }
55
+
56
+ public function getVersionWithShortReference(): string
57
+ {
58
+ return $this->prettyVersion . '@' . $this->getShortReference();
59
+ }
60
+
61
+ public function getPackageName(): string
62
+ {
63
+ return $this->packageName;
64
+ }
65
+
66
+ public function getShortVersion(): string
67
+ {
68
+ return $this->prettyVersion;
69
+ }
70
+
71
+ /**
72
+ * @deprecated
73
+ */
74
+ public function getCommitHash(): string
75
+ {
76
+ return $this->getReference();
77
+ }
78
+
79
+ public function getReference(): string
80
+ {
81
+ return $this->reference;
82
+ }
83
+
84
+ /**
85
+ * @deprecated
86
+ */
87
+ public function getShortCommitHash(): string
88
+ {
89
+ return $this->getShortReference();
90
+ }
91
+
92
+ public function getShortReference(): string
93
+ {
94
+ return substr($this->reference, 0, self::SHORT_COMMIT_LENGTH);
95
+ }
96
+
97
+ public function __toString(): string
98
+ {
99
+ return $this->getPrettyVersion();
100
+ }
101
+ }
src/vendor/php-http/client-common/.php_cs.dist ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Configuration for fabpot/php-cs-fixer
5
+ *
6
+ * @link https://github.com/FriendsOfPHP/PHP-CS-Fixer
7
+ */
8
+
9
+ $finder = PhpCsFixer\Finder::create()
10
+ ->in('src')
11
+ ->in('spec')
12
+ ;
13
+ return PhpCsFixer\Config::create()
14
+ ->setRules([
15
+ '@PSR2' => true,
16
+ '@Symfony' => true,
17
+ 'array_syntax' => [
18
+ 'syntax' => 'short',
19
+ ],
20
+ 'no_empty_phpdoc' => true,
21
+ 'phpdoc_to_comment' => false,
22
+ 'single_line_throw' => false,
23
+ ])
24
+ ->setFinder($finder);
src/vendor/php-http/client-common/CHANGELOG.md ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Change Log
2
+
3
+ ## 2.5.0 - 2021-11-26
4
+
5
+ ### Added
6
+
7
+ - Support for Symfony 6
8
+ - Support for PHP 8.1
9
+
10
+ ### Changed
11
+
12
+ - Dropped support for Symfony 2 and 3 - please keep using version 2.4.0 of this library if you can't update Symfony.
13
+
14
+ ## 2.4.0 - 2021-07-05
15
+
16
+ ### Added
17
+
18
+ - `strict` option to `RedirectPlugin` to allow preserving the request method on redirections with status 300, 301 and 302.
19
+
20
+ ## 2.3.0 - 2020-07-21
21
+
22
+ ### Fixed
23
+
24
+ - HttpMethodsClient with PSR RequestFactory
25
+ - Bug in the cookie plugin with empty cookies
26
+ - Bug when parsing null-valued date headers
27
+
28
+ ### Changed
29
+
30
+ - Deprecation when constructing a HttpMethodsClient with PSR RequestFactory but without a StreamFactory
31
+
32
+ ## 2.2.1 - 2020-07-13
33
+
34
+ ### Fixed
35
+
36
+ - Support for PHP 8
37
+ - Plugin callable phpdoc
38
+
39
+ ## 2.2.0 - 2020-07-02
40
+
41
+ ### Added
42
+
43
+ - Plugin client builder for making a `PluginClient`
44
+ - Support for the PSR-17 request factory in `HttpMethodsClient`
45
+
46
+ ### Changed
47
+
48
+ - Restored support for `symfony/options-resolver: ^2.6`
49
+ - Consistent implementation of union type checking
50
+
51
+ ### Fixed
52
+
53
+ - Memory leak when using the `PluginClient` with plugins
54
+
55
+ ## 2.1.0 - 2019-11-18
56
+
57
+ ### Added
58
+
59
+ - Support Symfony 5
60
+
61
+ ## 2.0.0 - 2019-02-03
62
+
63
+ ### Changed
64
+
65
+ - HttpClientRouter now throws a HttpClientNoMatchException instead of a RequestException if it can not find a client for the request.
66
+ - RetryPlugin will only retry exceptions when there is no response, or a response in the 5xx HTTP code range.
67
+ - RetryPlugin also retries when no exception is thrown if the responses has HTTP code in the 5xx range.
68
+ The callbacks for exception handling have been renamed and callbacks for response handling have been added.
69
+ - Abstract method `HttpClientPool::chooseHttpClient()` has now an explicit return type (`Http\Client\Common\HttpClientPoolItem`)
70
+ - Interface method `Plugin::handleRequest(...)` has now an explicit return type (`Http\Promise\Promise`)
71
+ - Made classes final that are not intended to be extended.
72
+ - Added interfaces for BatchClient, HttpClientRouter and HttpMethodsClient.
73
+ (These interfaces use the `Interface` suffix to avoid name collisions.)
74
+ - Added an interface for HttpClientPool and moved the abstract class to the HttpClientPool sub namespace.
75
+ - AddPathPlugin: Do not add the prefix if the URL already has the same prefix.
76
+ - All exceptions in `Http\Client\Common\Exception` are final.
77
+
78
+ ### Removed
79
+
80
+ - Deprecated option `debug_plugins` has been removed from `PluginClient`
81
+ - Deprecated options `decider` and `delay` have been removed from `RetryPlugin`, use `exception_decider` and `exception_delay` instead.
82
+
83
+ ## 1.11.0 - 2021-07-11
84
+
85
+ ### Changed
86
+
87
+ - Backported from version 2: AddPathPlugin: Do not add the prefix if the URL already has the same prefix.
88
+
89
+ ## 1.10.0 - 2019-11-18
90
+
91
+ ### Added
92
+
93
+ - Support for Symfony 5
94
+
95
+ ## 1.9.1 - 2019-02-02
96
+
97
+ ### Added
98
+
99
+ - Updated type hints in doc blocks.
100
+
101
+ ## 1.9.0 - 2019-01-03
102
+
103
+ ### Added
104
+
105
+ - Support for PSR-18 clients
106
+ - Added traits `VersionBridgePlugin` and `VersionBridgeClient` to help plugins and clients to support both
107
+ 1.x and 2.x version of `php-http/client-common` and `php-http/httplug`.
108
+
109
+ ### Changed
110
+
111
+ - RetryPlugin: Renamed the configuration options for the exception retry callback from `decider` to `exception_decider`
112
+ and `delay` to `exception_delay`. The old names still work but are deprecated.
113
+
114
+ ## 1.8.2 - 2018-12-14
115
+
116
+ ### Changed
117
+
118
+ - When multiple cookies exist, a single header with all cookies is sent as per RFC 6265 Section 5.4
119
+ - AddPathPlugin will now trim of ending slashes in paths
120
+
121
+ ## 1.8.1 - 2018-10-09
122
+
123
+ ### Fixed
124
+
125
+ - Reverted change to RetryPlugin so it again waits when retrying to avoid "can only throw objects" error.
126
+
127
+ ## 1.8.0 - 2018-09-21
128
+
129
+ ### Added
130
+
131
+ - Add an option on ErrorPlugin to only throw exception on response with 5XX status code.
132
+
133
+ ### Changed
134
+
135
+ - AddPathPlugin no longer add prefix multiple times if a request is restarted - it now only adds the prefix if that request chain has not yet passed through the AddPathPlugin
136
+ - RetryPlugin no longer wait for retried requests and use a deferred promise instead
137
+
138
+ ### Fixed
139
+
140
+ - Decoder plugin will now remove header when there is no more encoding, instead of setting to an empty array
141
+
142
+ ## 1.7.0 - 2017-11-30
143
+
144
+ ### Added
145
+
146
+ - Symfony 4 support
147
+
148
+ ### Changed
149
+
150
+ - Strict comparison in DecoderPlugin
151
+
152
+ ## 1.6.0 - 2017-10-16
153
+
154
+ ### Added
155
+
156
+ - Add HttpClientPool client to leverage load balancing and fallback mechanism [see the documentation](http://docs.php-http.org/en/latest/components/client-common.html) for more details.
157
+ - `PluginClientFactory` to create `PluginClient` instances.
158
+ - Added new option 'delay' for `RetryPlugin`.
159
+ - Added new option 'decider' for `RetryPlugin`.
160
+ - Supports more cookie date formats in the Cookie Plugin
161
+
162
+ ### Changed
163
+
164
+ - The `RetryPlugin` does now wait between retries. To disable/change this feature you must write something like:
165
+
166
+ ```php
167
+ $plugin = new RetryPlugin(['delay' => function(RequestInterface $request, Exception $e, $retries) {
168
+ return 0;
169
+ });
170
+ ```
171
+
172
+ ### Deprecated
173
+
174
+ - The `debug_plugins` option for `PluginClient` is deprecated and will be removed in 2.0. Use the decorator design pattern instead like in [ProfilePlugin](https://github.com/php-http/HttplugBundle/blob/de33f9c14252f22093a5ec7d84f17535ab31a384/Collector/ProfilePlugin.php).
175
+
176
+ ## 1.5.0 - 2017-03-30
177
+
178
+ ### Added
179
+
180
+ - `QueryDefaultsPlugin` to add default query parameters.
181
+
182
+ ## 1.4.2 - 2017-03-18
183
+
184
+ ### Deprecated
185
+
186
+ - `DecoderPlugin` does not longer claim to support `compress` content encoding
187
+
188
+ ### Fixed
189
+
190
+ - `CookiePlugin` allows main domain cookies to be sent/stored for subdomains
191
+ - `DecoderPlugin` uses the right `FilteredStream` to handle `deflate` content encoding
192
+
193
+
194
+ ## 1.4.1 - 2017-02-20
195
+
196
+ ### Fixed
197
+
198
+ - Cast return value of `StreamInterface::getSize` to string in `ContentLengthPlugin`
199
+
200
+
201
+ ## 1.4.0 - 2016-11-04
202
+
203
+ ### Added
204
+
205
+ - Add Path plugin
206
+ - Base URI plugin that combines Add Host and Add Path plugins
207
+
208
+
209
+ ## 1.3.0 - 2016-10-16
210
+
211
+ ### Changed
212
+
213
+ - Fix Emulated Trait to use Http based promise which respect the HttpAsyncClient interface
214
+ - Require Httplug 1.1 where we use HTTP specific promises.
215
+ - RedirectPlugin: use the full URL instead of the URI to properly keep track of redirects
216
+ - Add AddPathPlugin for API URLs with base path
217
+ - Add BaseUriPlugin that combines AddHostPlugin and AddPathPlugin
218
+
219
+
220
+ ## 1.2.1 - 2016-07-26
221
+
222
+ ### Changed
223
+
224
+ - AddHostPlugin also sets the port if specified
225
+
226
+
227
+ ## 1.2.0 - 2016-07-14
228
+
229
+ ### Added
230
+
231
+ - Suggest separate plugins in composer.json
232
+ - Introduced `debug_plugins` option for `PluginClient`
233
+
234
+
235
+ ## 1.1.0 - 2016-05-04
236
+
237
+ ### Added
238
+
239
+ - Add a flexible http client providing both contract, and only emulating what's necessary
240
+ - HTTP Client Router: route requests to underlying clients
241
+ - Plugin client and core plugins moved here from `php-http/plugins`
242
+
243
+ ### Deprecated
244
+
245
+ - Extending client classes, they will be made final in version 2.0
246
+
247
+
248
+ ## 1.0.0 - 2016-01-27
249
+
250
+ ### Changed
251
+
252
+ - Remove useless interface in BatchException
253
+
254
+
255
+ ## 0.2.0 - 2016-01-12
256
+
257
+ ### Changed
258
+
259
+ - Updated package files
260
+ - Updated HTTPlug to RC1
261
+
262
+
263
+ ## 0.1.1 - 2015-12-26
264
+
265
+ ### Added
266
+
267
+ - Emulated clients
268
+
269
+
270
+ ## 0.1.0 - 2015-12-25
271
+
272
+ ### Added
273
+
274
+ - Batch client from utils
275
+ - Methods client from utils
276
+ - Emulators and decorators from client-tools
src/vendor/php-http/client-common/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2015-2016 PHP HTTP Team <team@php-http.org>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
src/vendor/php-http/client-common/README.md ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HTTP Client Common
2
+
3
+ [![Latest Version](https://img.shields.io/github/release/php-http/client-common.svg?style=flat-square)](https://github.com/php-http/client-common/releases)
4
+ [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
5
+ [![Build Status](https://img.shields.io/travis/php-http/client-common/master.svg?style=flat-square)](https://travis-ci.org/php-http/client-common)
6
+ [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/client-common.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/client-common)
7
+ [![Quality Score](https://img.shields.io/scrutinizer/g/php-http/client-common.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/client-common)
8
+ [![Total Downloads](https://img.shields.io/packagist/dt/php-http/client-common.svg?style=flat-square)](https://packagist.org/packages/php-http/client-common)
9
+
10
+ **Common HTTP Client implementations and tools for HTTPlug.**
11
+
12
+
13
+ ## Install
14
+
15
+ Via Composer
16
+
17
+ ``` bash
18
+ $ composer require php-http/client-common
19
+ ```
20
+
21
+
22
+ ## Usage
23
+
24
+ This package provides common tools for HTTP Clients:
25
+
26
+ - BatchClient to handle sending requests in parallel
27
+ - A convenience client with HTTP method names as class methods
28
+ - Emulator, decorator layers for sync/async clients
29
+
30
+
31
+ ## Documentation
32
+
33
+ Please see the [official documentation](http://docs.php-http.org/en/latest/components/client-common.html).
34
+
35
+
36
+ ## Testing
37
+
38
+ ``` bash
39
+ $ composer test
40
+ ```
41
+
42
+
43
+ ## Contributing
44
+
45
+ Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
46
+
47
+
48
+ ## Security
49
+
50
+ If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
51
+
52
+
53
+ ## License
54
+
55
+ The MIT License (MIT). Please see [License File](LICENSE) for more information.
src/vendor/php-http/client-common/composer.json ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "php-http/client-common",
3
+ "description": "Common HTTP Client implementations and tools for HTTPlug",
4
+ "license": "MIT",
5
+ "keywords": ["http", "client", "httplug", "common"],
6
+ "homepage": "http://httplug.io",
7
+ "authors": [
8
+ {
9
+ "name": "Márk Sági-Kazár",
10
+ "email": "mark.sagikazar@gmail.com"
11
+ }
12
+ ],
13
+ "require": {
14
+ "php": "^7.1 || ^8.0",
15
+ "php-http/httplug": "^2.0",
16
+ "php-http/message": "^1.6",
17
+ "php-http/message-factory": "^1.0",
18
+ "psr/http-client": "^1.0",
19
+ "psr/http-factory": "^1.0",
20
+ "psr/http-message": "^1.0",
21
+ "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0",
22
+ "symfony/polyfill-php80": "^1.17"
23
+ },
24
+ "require-dev": {
25
+ "doctrine/instantiator": "^1.1",
26
+ "guzzlehttp/psr7": "^1.4",
27
+ "nyholm/psr7": "^1.2",
28
+ "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
29
+ "phpspec/prophecy": "^1.10.2",
30
+ "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3"
31
+ },
32
+ "suggest": {
33
+ "ext-json": "To detect JSON responses with the ContentTypePlugin",
34
+ "ext-libxml": "To detect XML responses with the ContentTypePlugin",
35
+ "php-http/logger-plugin": "PSR-3 Logger plugin",
36
+ "php-http/cache-plugin": "PSR-6 Cache plugin",
37
+ "php-http/stopwatch-plugin": "Symfony Stopwatch plugin"
38
+ },
39
+ "autoload": {
40
+ "psr-4": {
41
+ "Http\\Client\\Common\\": "src/"
42
+ }
43
+ },
44
+ "autoload-dev": {
45
+ "psr-4": {
46
+ "spec\\Http\\Client\\Common\\": "spec/"
47
+ }
48
+ },
49
+ "scripts": {
50
+ "test": [
51
+ "vendor/bin/phpspec run",
52
+ "vendor/bin/phpunit"
53
+ ],
54
+ "test-ci": [
55
+ "vendor/bin/phpspec run -c phpspec.ci.yml",
56
+ "vendor/bin/phpunit"
57
+ ]
58
+ },
59
+ "config": {
60
+ "sort-packages": true
61
+ },
62
+ "extra": {
63
+ "branch-alias": {
64
+ "dev-master": "2.3.x-dev"
65
+ }
66
+ }
67
+ }
src/vendor/php-http/client-common/src/BatchClient.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\Common\Exception\BatchException;
8
+ use Psr\Http\Client\ClientExceptionInterface;
9
+ use Psr\Http\Client\ClientInterface;
10
+
11
+ final class BatchClient implements BatchClientInterface
12
+ {
13
+ /**
14
+ * @var ClientInterface
15
+ */
16
+ private $client;
17
+
18
+ public function __construct(ClientInterface $client)
19
+ {
20
+ $this->client = $client;
21
+ }
22
+
23
+ public function sendRequests(array $requests): BatchResult
24
+ {
25
+ $batchResult = new BatchResult();
26
+
27
+ foreach ($requests as $request) {
28
+ try {
29
+ $response = $this->client->sendRequest($request);
30
+ $batchResult = $batchResult->addResponse($request, $response);
31
+ } catch (ClientExceptionInterface $e) {
32
+ $batchResult = $batchResult->addException($request, $e);
33
+ }
34
+ }
35
+
36
+ if ($batchResult->hasExceptions()) {
37
+ throw new BatchException($batchResult);
38
+ }
39
+
40
+ return $batchResult;
41
+ }
42
+ }
src/vendor/php-http/client-common/src/BatchClientInterface.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\Common\Exception\BatchException;
8
+ use Psr\Http\Message\RequestInterface;
9
+
10
+ /**
11
+ * BatchClient allow to sends multiple request and retrieve a Batch Result.
12
+ *
13
+ * This implementation simply loops over the requests and uses sendRequest with each of them.
14
+ *
15
+ * @author Joel Wurtz <jwurtz@jolicode.com>
16
+ */
17
+ interface BatchClientInterface
18
+ {
19
+ /**
20
+ * Send several requests.
21
+ *
22
+ * You may not assume that the requests are executed in a particular order. If the order matters
23
+ * for your application, use sendRequest sequentially.
24
+ *
25
+ * @param RequestInterface[] $requests The requests to send
26
+ *
27
+ * @return BatchResult Containing one result per request
28
+ *
29
+ * @throws BatchException If one or more requests fails. The exception gives access to the
30
+ * BatchResult with a map of request to result for success, request to
31
+ * exception for failures
32
+ */
33
+ public function sendRequests(array $requests): BatchResult;
34
+ }
src/vendor/php-http/client-common/src/BatchResult.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Psr\Http\Client\ClientExceptionInterface;
8
+ use Psr\Http\Message\RequestInterface;
9
+ use Psr\Http\Message\ResponseInterface;
10
+
11
+ /**
12
+ * Responses and exceptions returned from parallel request execution.
13
+ *
14
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
15
+ */
16
+ final class BatchResult
17
+ {
18
+ /**
19
+ * @var \SplObjectStorage<RequestInterface, ResponseInterface>
20
+ */
21
+ private $responses;
22
+
23
+ /**
24
+ * @var \SplObjectStorage<RequestInterface, ClientExceptionInterface>
25
+ */
26
+ private $exceptions;
27
+
28
+ public function __construct()
29
+ {
30
+ $this->responses = new \SplObjectStorage();
31
+ $this->exceptions = new \SplObjectStorage();
32
+ }
33
+
34
+ /**
35
+ * Checks if there are any successful responses at all.
36
+ */
37
+ public function hasResponses(): bool
38
+ {
39
+ return $this->responses->count() > 0;
40
+ }
41
+
42
+ /**
43
+ * Returns all successful responses.
44
+ *
45
+ * @return ResponseInterface[]
46
+ */
47
+ public function getResponses(): array
48
+ {
49
+ $responses = [];
50
+
51
+ foreach ($this->responses as $request) {
52
+ $responses[] = $this->responses[$request];
53
+ }
54
+
55
+ return $responses;
56
+ }
57
+
58
+ /**
59
+ * Checks if there is a successful response for a request.
60
+ */
61
+ public function isSuccessful(RequestInterface $request): bool
62
+ {
63
+ return $this->responses->contains($request);
64
+ }
65
+
66
+ /**
67
+ * Returns the response for a successful request.
68
+ *
69
+ * @throws \UnexpectedValueException If request was not part of the batch or failed
70
+ */
71
+ public function getResponseFor(RequestInterface $request): ResponseInterface
72
+ {
73
+ try {
74
+ return $this->responses[$request];
75
+ } catch (\UnexpectedValueException $e) {
76
+ throw new \UnexpectedValueException('Request not found', $e->getCode(), $e);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Adds a response in an immutable way.
82
+ *
83
+ * @return BatchResult the new BatchResult with this request-response pair added to it
84
+ */
85
+ public function addResponse(RequestInterface $request, ResponseInterface $response): self
86
+ {
87
+ $new = clone $this;
88
+ $new->responses->attach($request, $response);
89
+
90
+ return $new;
91
+ }
92
+
93
+ /**
94
+ * Checks if there are any unsuccessful requests at all.
95
+ */
96
+ public function hasExceptions(): bool
97
+ {
98
+ return $this->exceptions->count() > 0;
99
+ }
100
+
101
+ /**
102
+ * Returns all exceptions for the unsuccessful requests.
103
+ *
104
+ * @return ClientExceptionInterface[]
105
+ */
106
+ public function getExceptions(): array
107
+ {
108
+ $exceptions = [];
109
+
110
+ foreach ($this->exceptions as $request) {
111
+ $exceptions[] = $this->exceptions[$request];
112
+ }
113
+
114
+ return $exceptions;
115
+ }
116
+
117
+ /**
118
+ * Checks if there is an exception for a request, meaning the request failed.
119
+ */
120
+ public function isFailed(RequestInterface $request): bool
121
+ {
122
+ return $this->exceptions->contains($request);
123
+ }
124
+
125
+ /**
126
+ * Returns the exception for a failed request.
127
+ *
128
+ * @throws \UnexpectedValueException If request was not part of the batch or was successful
129
+ */
130
+ public function getExceptionFor(RequestInterface $request): ClientExceptionInterface
131
+ {
132
+ try {
133
+ return $this->exceptions[$request];
134
+ } catch (\UnexpectedValueException $e) {
135
+ throw new \UnexpectedValueException('Request not found', $e->getCode(), $e);
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Adds an exception in an immutable way.
141
+ *
142
+ * @return BatchResult the new BatchResult with this request-exception pair added to it
143
+ */
144
+ public function addException(RequestInterface $request, ClientExceptionInterface $exception): self
145
+ {
146
+ $new = clone $this;
147
+ $new->exceptions->attach($request, $exception);
148
+
149
+ return $new;
150
+ }
151
+
152
+ public function __clone()
153
+ {
154
+ $this->responses = clone $this->responses;
155
+ $this->exceptions = clone $this->exceptions;
156
+ }
157
+ }
src/vendor/php-http/client-common/src/Deferred.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Promise\Promise;
8
+ use Psr\Http\Client\ClientExceptionInterface;
9
+ use Psr\Http\Message\ResponseInterface;
10
+
11
+ /**
12
+ * A deferred allow to return a promise which has not been resolved yet.
13
+ */
14
+ final class Deferred implements Promise
15
+ {
16
+ /**
17
+ * @var ResponseInterface|null
18
+ */
19
+ private $value;
20
+
21
+ /**
22
+ * @var ClientExceptionInterface|null
23
+ */
24
+ private $failure;
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ private $state;
30
+
31
+ /**
32
+ * @var callable
33
+ */
34
+ private $waitCallback;
35
+
36
+ /**
37
+ * @var callable[]
38
+ */
39
+ private $onFulfilledCallbacks;
40
+
41
+ /**
42
+ * @var callable[]
43
+ */
44
+ private $onRejectedCallbacks;
45
+
46
+ public function __construct(callable $waitCallback)
47
+ {
48
+ $this->waitCallback = $waitCallback;
49
+ $this->state = Promise::PENDING;
50
+ $this->onFulfilledCallbacks = [];
51
+ $this->onRejectedCallbacks = [];
52
+ }
53
+
54
+ /**
55
+ * {@inheritdoc}
56
+ */
57
+ public function then(callable $onFulfilled = null, callable $onRejected = null): Promise
58
+ {
59
+ $deferred = new self($this->waitCallback);
60
+
61
+ $this->onFulfilledCallbacks[] = function (ResponseInterface $response) use ($onFulfilled, $deferred) {
62
+ try {
63
+ if (null !== $onFulfilled) {
64
+ $response = $onFulfilled($response);
65
+ }
66
+ $deferred->resolve($response);
67
+ } catch (ClientExceptionInterface $exception) {
68
+ $deferred->reject($exception);
69
+ }
70
+ };
71
+
72
+ $this->onRejectedCallbacks[] = function (ClientExceptionInterface $exception) use ($onRejected, $deferred) {
73
+ try {
74
+ if (null !== $onRejected) {
75
+ $response = $onRejected($exception);
76
+ $deferred->resolve($response);
77
+
78
+ return;
79
+ }
80
+ $deferred->reject($exception);
81
+ } catch (ClientExceptionInterface $newException) {
82
+ $deferred->reject($newException);
83
+ }
84
+ };
85
+
86
+ return $deferred;
87
+ }
88
+
89
+ /**
90
+ * {@inheritdoc}
91
+ */
92
+ public function getState(): string
93
+ {
94
+ return $this->state;
95
+ }
96
+
97
+ /**
98
+ * Resolve this deferred with a Response.
99
+ */
100
+ public function resolve(ResponseInterface $response): void
101
+ {
102
+ if (Promise::PENDING !== $this->state) {
103
+ return;
104
+ }
105
+
106
+ $this->value = $response;
107
+ $this->state = Promise::FULFILLED;
108
+
109
+ foreach ($this->onFulfilledCallbacks as $onFulfilledCallback) {
110
+ $onFulfilledCallback($response);
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Reject this deferred with an Exception.
116
+ */
117
+ public function reject(ClientExceptionInterface $exception): void
118
+ {
119
+ if (Promise::PENDING !== $this->state) {
120
+ return;
121
+ }
122
+
123
+ $this->failure = $exception;
124
+ $this->state = Promise::REJECTED;
125
+
126
+ foreach ($this->onRejectedCallbacks as $onRejectedCallback) {
127
+ $onRejectedCallback($exception);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * {@inheritdoc}
133
+ */
134
+ public function wait($unwrap = true)
135
+ {
136
+ if (Promise::PENDING === $this->state) {
137
+ $callback = $this->waitCallback;
138
+ $callback();
139
+ }
140
+
141
+ if (!$unwrap) {
142
+ return null;
143
+ }
144
+
145
+ if (Promise::FULFILLED === $this->state) {
146
+ return $this->value;
147
+ }
148
+
149
+ /** @var ClientExceptionInterface */
150
+ throw $this->failure;
151
+ }
152
+ }
src/vendor/php-http/client-common/src/EmulatedHttpAsyncClient.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\HttpAsyncClient;
8
+ use Http\Client\HttpClient;
9
+ use Psr\Http\Client\ClientInterface;
10
+
11
+ /**
12
+ * Emulates an async HTTP client with the help of a synchronous client.
13
+ *
14
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
15
+ */
16
+ final class EmulatedHttpAsyncClient implements HttpClient, HttpAsyncClient
17
+ {
18
+ use HttpAsyncClientEmulator;
19
+ use HttpClientDecorator;
20
+
21
+ public function __construct(ClientInterface $httpClient)
22
+ {
23
+ $this->httpClient = $httpClient;
24
+ }
25
+ }
src/vendor/php-http/client-common/src/EmulatedHttpClient.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\HttpAsyncClient;
8
+ use Http\Client\HttpClient;
9
+
10
+ /**
11
+ * Emulates a synchronous HTTP client with the help of an asynchronous client.
12
+ *
13
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
14
+ */
15
+ final class EmulatedHttpClient implements HttpClient, HttpAsyncClient
16
+ {
17
+ use HttpAsyncClientDecorator;
18
+ use HttpClientEmulator;
19
+
20
+ public function __construct(HttpAsyncClient $httpAsyncClient)
21
+ {
22
+ $this->httpAsyncClient = $httpAsyncClient;
23
+ }
24
+ }
src/vendor/php-http/client-common/src/Exception/BatchException.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Exception;
6
+
7
+ use Http\Client\Common\BatchResult;
8
+ use Http\Client\Exception\TransferException;
9
+
10
+ /**
11
+ * This exception is thrown when HttpClient::sendRequests led to at least one failure.
12
+ *
13
+ * It gives access to a BatchResult with the request-exception and request-response pairs.
14
+ *
15
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
16
+ */
17
+ final class BatchException extends TransferException
18
+ {
19
+ /**
20
+ * @var BatchResult
21
+ */
22
+ private $result;
23
+
24
+ public function __construct(BatchResult $result)
25
+ {
26
+ $this->result = $result;
27
+ parent::__construct();
28
+ }
29
+
30
+ /**
31
+ * Returns the BatchResult that contains all responses and exceptions.
32
+ */
33
+ public function getResult(): BatchResult
34
+ {
35
+ return $this->result;
36
+ }
37
+ }
src/vendor/php-http/client-common/src/Exception/CircularRedirectionException.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Exception;
6
+
7
+ use Http\Client\Exception\HttpException;
8
+
9
+ /**
10
+ * Thrown when circular redirection is detected.
11
+ *
12
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
13
+ */
14
+ final class CircularRedirectionException extends HttpException
15
+ {
16
+ }
src/vendor/php-http/client-common/src/Exception/ClientErrorException.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Exception;
6
+
7
+ use Http\Client\Exception\HttpException;
8
+
9
+ /**
10
+ * Thrown when there is a client error (4xx).
11
+ *
12
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
13
+ */
14
+ final class ClientErrorException extends HttpException
15
+ {
16
+ }
src/vendor/php-http/client-common/src/Exception/HttpClientNoMatchException.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Exception;
6
+
7
+ use Http\Client\Exception\TransferException;
8
+ use Psr\Http\Message\RequestInterface;
9
+
10
+ /**
11
+ * Thrown when a http client match in the HTTPClientRouter.
12
+ *
13
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
14
+ */
15
+ final class HttpClientNoMatchException extends TransferException
16
+ {
17
+ /**
18
+ * @var RequestInterface
19
+ */
20
+ private $request;
21
+
22
+ public function __construct(string $message, RequestInterface $request, \Exception $previous = null)
23
+ {
24
+ $this->request = $request;
25
+
26
+ parent::__construct($message, 0, $previous);
27
+ }
28
+
29
+ public function getRequest(): RequestInterface
30
+ {
31
+ return $this->request;
32
+ }
33
+ }
src/vendor/php-http/client-common/src/Exception/HttpClientNotFoundException.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Exception;
6
+
7
+ use Http\Client\Exception\TransferException;
8
+
9
+ /**
10
+ * Thrown when a http client cannot be chosen in a pool.
11
+ *
12
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
13
+ */
14
+ final class HttpClientNotFoundException extends TransferException
15
+ {
16
+ }
src/vendor/php-http/client-common/src/Exception/LoopException.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Exception;
6
+
7
+ use Http\Client\Exception\RequestException;
8
+
9
+ /**
10
+ * Thrown when the Plugin Client detects an endless loop.
11
+ *
12
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
13
+ */
14
+ final class LoopException extends RequestException
15
+ {
16
+ }
src/vendor/php-http/client-common/src/Exception/MultipleRedirectionException.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Exception;
6
+
7
+ use Http\Client\Exception\HttpException;
8
+
9
+ /**
10
+ * Redirect location cannot be chosen.
11
+ *
12
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
13
+ */
14
+ final class MultipleRedirectionException extends HttpException
15
+ {
16
+ }
src/vendor/php-http/client-common/src/Exception/ServerErrorException.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Exception;
6
+
7
+ use Http\Client\Exception\HttpException;
8
+
9
+ /**
10
+ * Thrown when there is a server error (5xx).
11
+ *
12
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
13
+ */
14
+ final class ServerErrorException extends HttpException
15
+ {
16
+ }
src/vendor/php-http/client-common/src/FlexibleHttpClient.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\HttpAsyncClient;
8
+ use Http\Client\HttpClient;
9
+ use Psr\Http\Client\ClientInterface;
10
+
11
+ /**
12
+ * A flexible http client, which implements both interface and will emulate
13
+ * one contract, the other, or none at all depending on the injected client contract.
14
+ *
15
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
16
+ */
17
+ final class FlexibleHttpClient implements HttpClient, HttpAsyncClient
18
+ {
19
+ use HttpClientDecorator;
20
+ use HttpAsyncClientDecorator;
21
+
22
+ /**
23
+ * @param ClientInterface|HttpAsyncClient $client
24
+ */
25
+ public function __construct($client)
26
+ {
27
+ if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
28
+ throw new \TypeError(
29
+ sprintf('%s::__construct(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
30
+ );
31
+ }
32
+
33
+ $this->httpClient = $client instanceof ClientInterface ? $client : new EmulatedHttpClient($client);
34
+ $this->httpAsyncClient = $client instanceof HttpAsyncClient ? $client : new EmulatedHttpAsyncClient($client);
35
+ }
36
+ }
src/vendor/php-http/client-common/src/HttpAsyncClientDecorator.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\HttpAsyncClient;
8
+ use Psr\Http\Message\RequestInterface;
9
+
10
+ /**
11
+ * Decorates an HTTP Async Client.
12
+ *
13
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
14
+ */
15
+ trait HttpAsyncClientDecorator
16
+ {
17
+ /**
18
+ * @var HttpAsyncClient
19
+ */
20
+ protected $httpAsyncClient;
21
+
22
+ /**
23
+ * {@inheritdoc}
24
+ *
25
+ * @see HttpAsyncClient::sendAsyncRequest
26
+ */
27
+ public function sendAsyncRequest(RequestInterface $request)
28
+ {
29
+ return $this->httpAsyncClient->sendAsyncRequest($request);
30
+ }
31
+ }
src/vendor/php-http/client-common/src/HttpAsyncClientEmulator.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\Exception;
8
+ use Http\Client\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+ use Psr\Http\Message\ResponseInterface;
11
+
12
+ /**
13
+ * Emulates an HTTP Async Client in an HTTP Client.
14
+ *
15
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
16
+ */
17
+ trait HttpAsyncClientEmulator
18
+ {
19
+ /**
20
+ * {@inheritdoc}
21
+ *
22
+ * @see HttpClient::sendRequest
23
+ */
24
+ abstract public function sendRequest(RequestInterface $request): ResponseInterface;
25
+
26
+ /**
27
+ * {@inheritdoc}
28
+ *
29
+ * @see HttpAsyncClient::sendAsyncRequest
30
+ */
31
+ public function sendAsyncRequest(RequestInterface $request)
32
+ {
33
+ try {
34
+ return new Promise\HttpFulfilledPromise($this->sendRequest($request));
35
+ } catch (Exception $e) {
36
+ return new Promise\HttpRejectedPromise($e);
37
+ }
38
+ }
39
+ }
src/vendor/php-http/client-common/src/HttpClientDecorator.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Psr\Http\Client\ClientInterface;
8
+ use Psr\Http\Message\RequestInterface;
9
+ use Psr\Http\Message\ResponseInterface;
10
+
11
+ /**
12
+ * Decorates an HTTP Client.
13
+ *
14
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
15
+ */
16
+ trait HttpClientDecorator
17
+ {
18
+ /**
19
+ * @var ClientInterface
20
+ */
21
+ protected $httpClient;
22
+
23
+ /**
24
+ * {@inheritdoc}
25
+ *
26
+ * @see ClientInterface::sendRequest
27
+ */
28
+ public function sendRequest(RequestInterface $request): ResponseInterface
29
+ {
30
+ return $this->httpClient->sendRequest($request);
31
+ }
32
+ }
src/vendor/php-http/client-common/src/HttpClientEmulator.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Psr\Http\Message\RequestInterface;
8
+ use Psr\Http\Message\ResponseInterface;
9
+
10
+ /**
11
+ * Emulates an HTTP Client in an HTTP Async Client.
12
+ *
13
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
14
+ */
15
+ trait HttpClientEmulator
16
+ {
17
+ /**
18
+ * {@inheritdoc}
19
+ *
20
+ * @see HttpClient::sendRequest
21
+ */
22
+ public function sendRequest(RequestInterface $request): ResponseInterface
23
+ {
24
+ $promise = $this->sendAsyncRequest($request);
25
+
26
+ return $promise->wait();
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ *
32
+ * @see HttpAsyncClient::sendAsyncRequest
33
+ */
34
+ abstract public function sendAsyncRequest(RequestInterface $request);
35
+ }
src/vendor/php-http/client-common/src/HttpClientPool.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\Common\HttpClientPool\HttpClientPoolItem;
8
+ use Http\Client\HttpAsyncClient;
9
+ use Http\Client\HttpClient;
10
+ use Psr\Http\Client\ClientInterface;
11
+
12
+ /**
13
+ * A http client pool allows to send requests on a pool of different http client using a specific strategy (least used,
14
+ * round robin, ...).
15
+ */
16
+ interface HttpClientPool extends HttpAsyncClient, HttpClient
17
+ {
18
+ /**
19
+ * Add a client to the pool.
20
+ *
21
+ * @param ClientInterface|HttpAsyncClient|HttpClientPoolItem $client
22
+ */
23
+ public function addHttpClient($client): void;
24
+ }
src/vendor/php-http/client-common/src/HttpClientPool/HttpClientPool.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\HttpClientPool;
6
+
7
+ use Http\Client\Common\Exception\HttpClientNotFoundException;
8
+ use Http\Client\Common\HttpClientPool as HttpClientPoolInterface;
9
+ use Http\Client\HttpAsyncClient;
10
+ use Psr\Http\Client\ClientInterface;
11
+ use Psr\Http\Message\RequestInterface;
12
+ use Psr\Http\Message\ResponseInterface;
13
+
14
+ /**
15
+ * A http client pool allows to send requests on a pool of different http client using a specific strategy (least used,
16
+ * round robin, ...).
17
+ */
18
+ abstract class HttpClientPool implements HttpClientPoolInterface
19
+ {
20
+ /**
21
+ * @var HttpClientPoolItem[]
22
+ */
23
+ protected $clientPool = [];
24
+
25
+ /**
26
+ * Add a client to the pool.
27
+ *
28
+ * @param ClientInterface|HttpAsyncClient $client
29
+ */
30
+ public function addHttpClient($client): void
31
+ {
32
+ // no need to check for HttpClientPoolItem here, since it extends the other interfaces
33
+ if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
34
+ throw new \TypeError(
35
+ sprintf('%s::addHttpClient(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
36
+ );
37
+ }
38
+
39
+ if (!$client instanceof HttpClientPoolItem) {
40
+ $client = new HttpClientPoolItem($client);
41
+ }
42
+
43
+ $this->clientPool[] = $client;
44
+ }
45
+
46
+ /**
47
+ * Return an http client given a specific strategy.
48
+ *
49
+ * @throws HttpClientNotFoundException When no http client has been found into the pool
50
+ *
51
+ * @return HttpClientPoolItem Return a http client that can do both sync or async
52
+ */
53
+ abstract protected function chooseHttpClient(): HttpClientPoolItem;
54
+
55
+ /**
56
+ * {@inheritdoc}
57
+ */
58
+ public function sendAsyncRequest(RequestInterface $request)
59
+ {
60
+ return $this->chooseHttpClient()->sendAsyncRequest($request);
61
+ }
62
+
63
+ /**
64
+ * {@inheritdoc}
65
+ */
66
+ public function sendRequest(RequestInterface $request): ResponseInterface
67
+ {
68
+ return $this->chooseHttpClient()->sendRequest($request);
69
+ }
70
+ }
src/vendor/php-http/client-common/src/HttpClientPool/HttpClientPoolItem.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\HttpClientPool;
6
+
7
+ use Http\Client\Common\FlexibleHttpClient;
8
+ use Http\Client\Exception;
9
+ use Http\Client\HttpAsyncClient;
10
+ use Http\Client\HttpClient;
11
+ use Psr\Http\Client\ClientInterface;
12
+ use Psr\Http\Message\RequestInterface;
13
+ use Psr\Http\Message\ResponseInterface;
14
+
15
+ /**
16
+ * A HttpClientPoolItem represent a HttpClient inside a Pool.
17
+ *
18
+ * It is disabled when a request failed and can be reenabled after a certain number of seconds.
19
+ * It also keep tracks of the current number of open requests the client is currently being sending
20
+ * (only usable for async method).
21
+ *
22
+ * This class is used internally in the client pools and is not supposed to be used anywhere else.
23
+ *
24
+ * @final
25
+ *
26
+ * @internal
27
+ *
28
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
29
+ */
30
+ class HttpClientPoolItem implements HttpClient, HttpAsyncClient
31
+ {
32
+ /**
33
+ * @var int Number of request this client is currently sending
34
+ */
35
+ private $sendingRequestCount = 0;
36
+
37
+ /**
38
+ * @var \DateTime|null Time when this client has been disabled or null if enable
39
+ */
40
+ private $disabledAt;
41
+
42
+ /**
43
+ * Number of seconds until this client is enabled again after an error.
44
+ *
45
+ * null: never reenable this client.
46
+ *
47
+ * @var int|null
48
+ */
49
+ private $reenableAfter;
50
+
51
+ /**
52
+ * @var FlexibleHttpClient A http client responding to async and sync request
53
+ */
54
+ private $client;
55
+
56
+ /**
57
+ * @param ClientInterface|HttpAsyncClient $client
58
+ * @param int|null $reenableAfter Number of seconds until this client is enabled again after an error
59
+ */
60
+ public function __construct($client, int $reenableAfter = null)
61
+ {
62
+ if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
63
+ throw new \TypeError(
64
+ sprintf('%s::__construct(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
65
+ );
66
+ }
67
+
68
+ $this->client = new FlexibleHttpClient($client);
69
+ $this->reenableAfter = $reenableAfter;
70
+ }
71
+
72
+ /**
73
+ * {@inheritdoc}
74
+ */
75
+ public function sendRequest(RequestInterface $request): ResponseInterface
76
+ {
77
+ if ($this->isDisabled()) {
78
+ throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request);
79
+ }
80
+
81
+ try {
82
+ $this->incrementRequestCount();
83
+ $response = $this->client->sendRequest($request);
84
+ $this->decrementRequestCount();
85
+ } catch (Exception $e) {
86
+ $this->disable();
87
+ $this->decrementRequestCount();
88
+
89
+ throw $e;
90
+ }
91
+
92
+ return $response;
93
+ }
94
+
95
+ /**
96
+ * {@inheritdoc}
97
+ */
98
+ public function sendAsyncRequest(RequestInterface $request)
99
+ {
100
+ if ($this->isDisabled()) {
101
+ throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request);
102
+ }
103
+
104
+ $this->incrementRequestCount();
105
+
106
+ return $this->client->sendAsyncRequest($request)->then(function ($response) {
107
+ $this->decrementRequestCount();
108
+
109
+ return $response;
110
+ }, function ($exception) {
111
+ $this->disable();
112
+ $this->decrementRequestCount();
113
+
114
+ throw $exception;
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Whether this client is disabled or not.
120
+ *
121
+ * If the client was disabled, calling this method checks if the client can
122
+ * be reenabled and if so enables it.
123
+ */
124
+ public function isDisabled(): bool
125
+ {
126
+ if (null !== $this->reenableAfter && null !== $this->disabledAt) {
127
+ // Reenable after a certain time
128
+ $now = new \DateTime();
129
+
130
+ if (($now->getTimestamp() - $this->disabledAt->getTimestamp()) >= $this->reenableAfter) {
131
+ $this->enable();
132
+
133
+ return false;
134
+ }
135
+
136
+ return true;
137
+ }
138
+
139
+ return null !== $this->disabledAt;
140
+ }
141
+
142
+ /**
143
+ * Get current number of request that are currently being sent by the underlying HTTP client.
144
+ */
145
+ public function getSendingRequestCount(): int
146
+ {
147
+ return $this->sendingRequestCount;
148
+ }
149
+
150
+ /**
151
+ * Increment the request count.
152
+ */
153
+ private function incrementRequestCount(): void
154
+ {
155
+ ++$this->sendingRequestCount;
156
+ }
157
+
158
+ /**
159
+ * Decrement the request count.
160
+ */
161
+ private function decrementRequestCount(): void
162
+ {
163
+ --$this->sendingRequestCount;
164
+ }
165
+
166
+ /**
167
+ * Enable the current client.
168
+ */
169
+ private function enable(): void
170
+ {
171
+ $this->disabledAt = null;
172
+ }
173
+
174
+ /**
175
+ * Disable the current client.
176
+ */
177
+ private function disable(): void
178
+ {
179
+ $this->disabledAt = new \DateTime('now');
180
+ }
181
+ }
src/vendor/php-http/client-common/src/HttpClientPool/LeastUsedClientPool.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\HttpClientPool;
6
+
7
+ use Http\Client\Common\Exception\HttpClientNotFoundException;
8
+
9
+ /**
10
+ * LeastUsedClientPool will choose the client with the less current request in the pool.
11
+ *
12
+ * This strategy is only useful when doing async request
13
+ *
14
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
15
+ */
16
+ final class LeastUsedClientPool extends HttpClientPool
17
+ {
18
+ /**
19
+ * {@inheritdoc}
20
+ */
21
+ protected function chooseHttpClient(): HttpClientPoolItem
22
+ {
23
+ $clientPool = array_filter($this->clientPool, function (HttpClientPoolItem $clientPoolItem) {
24
+ return !$clientPoolItem->isDisabled();
25
+ });
26
+
27
+ if (0 === count($clientPool)) {
28
+ throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool');
29
+ }
30
+
31
+ usort($clientPool, function (HttpClientPoolItem $clientA, HttpClientPoolItem $clientB) {
32
+ if ($clientA->getSendingRequestCount() === $clientB->getSendingRequestCount()) {
33
+ return 0;
34
+ }
35
+
36
+ if ($clientA->getSendingRequestCount() < $clientB->getSendingRequestCount()) {
37
+ return -1;
38
+ }
39
+
40
+ return 1;
41
+ });
42
+
43
+ return reset($clientPool);
44
+ }
45
+ }
src/vendor/php-http/client-common/src/HttpClientPool/RandomClientPool.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\HttpClientPool;
6
+
7
+ use Http\Client\Common\Exception\HttpClientNotFoundException;
8
+
9
+ /**
10
+ * RoundRobinClientPool will choose the next client in the pool.
11
+ *
12
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
13
+ */
14
+ final class RandomClientPool extends HttpClientPool
15
+ {
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ protected function chooseHttpClient(): HttpClientPoolItem
20
+ {
21
+ $clientPool = array_filter($this->clientPool, function (HttpClientPoolItem $clientPoolItem) {
22
+ return !$clientPoolItem->isDisabled();
23
+ });
24
+
25
+ if (0 === count($clientPool)) {
26
+ throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool');
27
+ }
28
+
29
+ return $clientPool[array_rand($clientPool)];
30
+ }
31
+ }
src/vendor/php-http/client-common/src/HttpClientPool/RoundRobinClientPool.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\HttpClientPool;
6
+
7
+ use Http\Client\Common\Exception\HttpClientNotFoundException;
8
+
9
+ /**
10
+ * RoundRobinClientPool will choose the next client in the pool.
11
+ *
12
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
13
+ */
14
+ final class RoundRobinClientPool extends HttpClientPool
15
+ {
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ protected function chooseHttpClient(): HttpClientPoolItem
20
+ {
21
+ $last = current($this->clientPool);
22
+
23
+ do {
24
+ $client = next($this->clientPool);
25
+
26
+ if (false === $client) {
27
+ $client = reset($this->clientPool);
28
+
29
+ if (false === $client) {
30
+ throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool');
31
+ }
32
+ }
33
+
34
+ // Case when there is only one and the last one has been disabled
35
+ if ($last === $client && $client->isDisabled()) {
36
+ throw new HttpClientNotFoundException('Cannot choose a http client as there is no one enabled in the pool');
37
+ }
38
+ } while ($client->isDisabled());
39
+
40
+ return $client;
41
+ }
42
+ }
src/vendor/php-http/client-common/src/HttpClientRouter.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\Common\Exception\HttpClientNoMatchException;
8
+ use Http\Client\HttpAsyncClient;
9
+ use Http\Message\RequestMatcher;
10
+ use Psr\Http\Client\ClientInterface;
11
+ use Psr\Http\Message\RequestInterface;
12
+ use Psr\Http\Message\ResponseInterface;
13
+
14
+ /**
15
+ * {@inheritdoc}
16
+ *
17
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
18
+ */
19
+ final class HttpClientRouter implements HttpClientRouterInterface
20
+ {
21
+ /**
22
+ * @var (array{matcher: RequestMatcher, client: FlexibleHttpClient})[]
23
+ */
24
+ private $clients = [];
25
+
26
+ /**
27
+ * {@inheritdoc}
28
+ */
29
+ public function sendRequest(RequestInterface $request): ResponseInterface
30
+ {
31
+ return $this->chooseHttpClient($request)->sendRequest($request);
32
+ }
33
+
34
+ /**
35
+ * {@inheritdoc}
36
+ */
37
+ public function sendAsyncRequest(RequestInterface $request)
38
+ {
39
+ return $this->chooseHttpClient($request)->sendAsyncRequest($request);
40
+ }
41
+
42
+ /**
43
+ * Add a client to the router.
44
+ *
45
+ * @param ClientInterface|HttpAsyncClient $client
46
+ */
47
+ public function addClient($client, RequestMatcher $requestMatcher): void
48
+ {
49
+ if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
50
+ throw new \TypeError(
51
+ sprintf('%s::addClient(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
52
+ );
53
+ }
54
+
55
+ $this->clients[] = [
56
+ 'matcher' => $requestMatcher,
57
+ 'client' => new FlexibleHttpClient($client),
58
+ ];
59
+ }
60
+
61
+ /**
62
+ * Choose an HTTP client given a specific request.
63
+ */
64
+ private function chooseHttpClient(RequestInterface $request): FlexibleHttpClient
65
+ {
66
+ foreach ($this->clients as $client) {
67
+ if ($client['matcher']->matches($request)) {
68
+ return $client['client'];
69
+ }
70
+ }
71
+
72
+ throw new HttpClientNoMatchException('No client found for the specified request', $request);
73
+ }
74
+ }
src/vendor/php-http/client-common/src/HttpClientRouterInterface.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\HttpAsyncClient;
8
+ use Http\Client\HttpClient;
9
+ use Http\Message\RequestMatcher;
10
+ use Psr\Http\Client\ClientInterface;
11
+
12
+ /**
13
+ * Route a request to a specific client in the stack based using a RequestMatcher.
14
+ *
15
+ * This is not a HttpClientPool client because it uses a matcher to select the client.
16
+ *
17
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
18
+ */
19
+ interface HttpClientRouterInterface extends HttpClient, HttpAsyncClient
20
+ {
21
+ /**
22
+ * Add a client to the router.
23
+ *
24
+ * @param ClientInterface|HttpAsyncClient $client
25
+ */
26
+ public function addClient($client, RequestMatcher $requestMatcher): void;
27
+ }
src/vendor/php-http/client-common/src/HttpMethodsClient.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Message\RequestFactory;
8
+ use Psr\Http\Client\ClientInterface;
9
+ use Psr\Http\Message\RequestFactoryInterface;
10
+ use Psr\Http\Message\RequestInterface;
11
+ use Psr\Http\Message\ResponseInterface;
12
+ use Psr\Http\Message\StreamFactoryInterface;
13
+ use Psr\Http\Message\StreamInterface;
14
+ use Psr\Http\Message\UriInterface;
15
+
16
+ final class HttpMethodsClient implements HttpMethodsClientInterface
17
+ {
18
+ /**
19
+ * @var ClientInterface
20
+ */
21
+ private $httpClient;
22
+
23
+ /**
24
+ * @var RequestFactory|RequestFactoryInterface
25
+ */
26
+ private $requestFactory;
27
+
28
+ /**
29
+ * @var StreamFactoryInterface|null
30
+ */
31
+ private $streamFactory;
32
+
33
+ /**
34
+ * @param RequestFactory|RequestFactoryInterface $requestFactory
35
+ */
36
+ public function __construct(ClientInterface $httpClient, $requestFactory, StreamFactoryInterface $streamFactory = null)
37
+ {
38
+ if (!$requestFactory instanceof RequestFactory && !$requestFactory instanceof RequestFactoryInterface) {
39
+ throw new \TypeError(
40
+ sprintf('%s::__construct(): Argument #2 ($requestFactory) must be of type %s|%s, %s given', self::class, RequestFactory::class, RequestFactoryInterface::class, get_debug_type($requestFactory))
41
+ );
42
+ }
43
+
44
+ if (!$requestFactory instanceof RequestFactory && null === $streamFactory) {
45
+ @trigger_error(sprintf('Passing a %s without a %s to %s::__construct() is deprecated as of version 2.3 and will be disallowed in version 3.0. A stream factory is required to create a request with a non-empty string body.', RequestFactoryInterface::class, StreamFactoryInterface::class, self::class));
46
+ }
47
+
48
+ $this->httpClient = $httpClient;
49
+ $this->requestFactory = $requestFactory;
50
+ $this->streamFactory = $streamFactory;
51
+ }
52
+
53
+ public function get($uri, array $headers = []): ResponseInterface
54
+ {
55
+ return $this->send('GET', $uri, $headers, null);
56
+ }
57
+
58
+ public function head($uri, array $headers = []): ResponseInterface
59
+ {
60
+ return $this->send('HEAD', $uri, $headers, null);
61
+ }
62
+
63
+ public function trace($uri, array $headers = []): ResponseInterface
64
+ {
65
+ return $this->send('TRACE', $uri, $headers, null);
66
+ }
67
+
68
+ public function post($uri, array $headers = [], $body = null): ResponseInterface
69
+ {
70
+ return $this->send('POST', $uri, $headers, $body);
71
+ }
72
+
73
+ public function put($uri, array $headers = [], $body = null): ResponseInterface
74
+ {
75
+ return $this->send('PUT', $uri, $headers, $body);
76
+ }
77
+
78
+ public function patch($uri, array $headers = [], $body = null): ResponseInterface
79
+ {
80
+ return $this->send('PATCH', $uri, $headers, $body);
81
+ }
82
+
83
+ public function delete($uri, array $headers = [], $body = null): ResponseInterface
84
+ {
85
+ return $this->send('DELETE', $uri, $headers, $body);
86
+ }
87
+
88
+ public function options($uri, array $headers = [], $body = null): ResponseInterface
89
+ {
90
+ return $this->send('OPTIONS', $uri, $headers, $body);
91
+ }
92
+
93
+ public function send(string $method, $uri, array $headers = [], $body = null): ResponseInterface
94
+ {
95
+ if (!is_string($uri) && !$uri instanceof UriInterface) {
96
+ throw new \TypeError(
97
+ sprintf('%s::send(): Argument #2 ($uri) must be of type string|%s, %s given', self::class, UriInterface::class, get_debug_type($uri))
98
+ );
99
+ }
100
+
101
+ if (!is_string($body) && !$body instanceof StreamInterface && null !== $body) {
102
+ throw new \TypeError(
103
+ sprintf('%s::send(): Argument #4 ($body) must be of type string|%s|null, %s given', self::class, StreamInterface::class, get_debug_type($body))
104
+ );
105
+ }
106
+
107
+ return $this->sendRequest(
108
+ self::createRequest($method, $uri, $headers, $body)
109
+ );
110
+ }
111
+
112
+ /**
113
+ * @param string|UriInterface $uri
114
+ * @param string|StreamInterface|null $body
115
+ */
116
+ private function createRequest(string $method, $uri, array $headers = [], $body = null): RequestInterface
117
+ {
118
+ if ($this->requestFactory instanceof RequestFactory) {
119
+ return $this->requestFactory->createRequest(
120
+ $method,
121
+ $uri,
122
+ $headers,
123
+ $body
124
+ );
125
+ }
126
+
127
+ $request = $this->requestFactory->createRequest($method, $uri);
128
+
129
+ foreach ($headers as $key => $value) {
130
+ $request = $request->withHeader($key, $value);
131
+ }
132
+
133
+ if (null !== $body && '' !== $body) {
134
+ if (null === $this->streamFactory) {
135
+ throw new \RuntimeException('Cannot create request: A stream factory is required to create a request with a non-empty string body.');
136
+ }
137
+
138
+ $request = $request->withBody(
139
+ is_string($body) ? $this->streamFactory->createStream($body) : $body
140
+ );
141
+ }
142
+
143
+ return $request;
144
+ }
145
+
146
+ public function sendRequest(RequestInterface $request): ResponseInterface
147
+ {
148
+ return $this->httpClient->sendRequest($request);
149
+ }
150
+ }
src/vendor/php-http/client-common/src/HttpMethodsClientInterface.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\Exception;
8
+ use Http\Client\HttpClient;
9
+ use Psr\Http\Message\ResponseInterface;
10
+ use Psr\Http\Message\StreamInterface;
11
+ use Psr\Http\Message\UriInterface;
12
+
13
+ /**
14
+ * Convenience HTTP client that integrates the MessageFactory in order to send
15
+ * requests in the following form:.
16
+ *
17
+ * $client
18
+ * ->get('/foo')
19
+ * ->post('/bar')
20
+ * ;
21
+ *
22
+ * The client also exposes the sendRequest methods of the wrapped HttpClient.
23
+ *
24
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
25
+ * @author David Buchmann <mail@davidbu.ch>
26
+ */
27
+ interface HttpMethodsClientInterface extends HttpClient
28
+ {
29
+ /**
30
+ * Sends a GET request.
31
+ *
32
+ * @param string|UriInterface $uri
33
+ *
34
+ * @throws Exception
35
+ */
36
+ public function get($uri, array $headers = []): ResponseInterface;
37
+
38
+ /**
39
+ * Sends an HEAD request.
40
+ *
41
+ * @param string|UriInterface $uri
42
+ *
43
+ * @throws Exception
44
+ */
45
+ public function head($uri, array $headers = []): ResponseInterface;
46
+
47
+ /**
48
+ * Sends a TRACE request.
49
+ *
50
+ * @param string|UriInterface $uri
51
+ *
52
+ * @throws Exception
53
+ */
54
+ public function trace($uri, array $headers = []): ResponseInterface;
55
+
56
+ /**
57
+ * Sends a POST request.
58
+ *
59
+ * @param string|UriInterface $uri
60
+ * @param string|StreamInterface|null $body
61
+ *
62
+ * @throws Exception
63
+ */
64
+ public function post($uri, array $headers = [], $body = null): ResponseInterface;
65
+
66
+ /**
67
+ * Sends a PUT request.
68
+ *
69
+ * @param string|UriInterface $uri
70
+ * @param string|StreamInterface|null $body
71
+ *
72
+ * @throws Exception
73
+ */
74
+ public function put($uri, array $headers = [], $body = null): ResponseInterface;
75
+
76
+ /**
77
+ * Sends a PATCH request.
78
+ *
79
+ * @param string|UriInterface $uri
80
+ * @param string|StreamInterface|null $body
81
+ *
82
+ * @throws Exception
83
+ */
84
+ public function patch($uri, array $headers = [], $body = null): ResponseInterface;
85
+
86
+ /**
87
+ * Sends a DELETE request.
88
+ *
89
+ * @param string|UriInterface $uri
90
+ * @param string|StreamInterface|null $body
91
+ *
92
+ * @throws Exception
93
+ */
94
+ public function delete($uri, array $headers = [], $body = null): ResponseInterface;
95
+
96
+ /**
97
+ * Sends an OPTIONS request.
98
+ *
99
+ * @param string|UriInterface $uri
100
+ * @param string|StreamInterface|null $body
101
+ *
102
+ * @throws Exception
103
+ */
104
+ public function options($uri, array $headers = [], $body = null): ResponseInterface;
105
+
106
+ /**
107
+ * Sends a request with any HTTP method.
108
+ *
109
+ * @param string $method HTTP method to use
110
+ * @param string|UriInterface $uri
111
+ * @param string|StreamInterface|null $body
112
+ *
113
+ * @throws Exception
114
+ */
115
+ public function send(string $method, $uri, array $headers = [], $body = null): ResponseInterface;
116
+ }
src/vendor/php-http/client-common/src/Plugin.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Promise\Promise;
8
+ use Psr\Http\Message\RequestInterface;
9
+
10
+ /**
11
+ * A plugin is a middleware to transform the request and/or the response.
12
+ *
13
+ * The plugin can:
14
+ * - break the chain and return a response
15
+ * - dispatch the request to the next middleware
16
+ * - restart the request
17
+ *
18
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
19
+ */
20
+ interface Plugin
21
+ {
22
+ /**
23
+ * Handle the request and return the response coming from the next callable.
24
+ *
25
+ * @see http://docs.php-http.org/en/latest/plugins/build-your-own.html
26
+ *
27
+ * @param callable(RequestInterface): Promise $next Next middleware in the chain, the request is passed as the first argument
28
+ * @param callable(RequestInterface): Promise $first First middleware in the chain, used to to restart a request
29
+ *
30
+ * @return Promise Resolves a PSR-7 Response or fails with an Http\Client\Exception (The same as HttpAsyncClient)
31
+ */
32
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise;
33
+ }
src/vendor/php-http/client-common/src/Plugin/AddHostPlugin.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+ use Psr\Http\Message\UriInterface;
11
+ use Symfony\Component\OptionsResolver\OptionsResolver;
12
+
13
+ /**
14
+ * Add schema, host and port to a request. Can be set to overwrite the schema and host if desired.
15
+ *
16
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
17
+ */
18
+ final class AddHostPlugin implements Plugin
19
+ {
20
+ /**
21
+ * @var UriInterface
22
+ */
23
+ private $host;
24
+
25
+ /**
26
+ * @var bool
27
+ */
28
+ private $replace;
29
+
30
+ /**
31
+ * @param array{'replace'?: bool} $config
32
+ *
33
+ * Configuration options:
34
+ * - replace: True will replace all hosts, false will only add host when none is specified.
35
+ */
36
+ public function __construct(UriInterface $host, array $config = [])
37
+ {
38
+ if ('' === $host->getHost()) {
39
+ throw new \LogicException('Host can not be empty');
40
+ }
41
+
42
+ $this->host = $host;
43
+
44
+ $resolver = new OptionsResolver();
45
+ $this->configureOptions($resolver);
46
+ $options = $resolver->resolve($config);
47
+
48
+ $this->replace = $options['replace'];
49
+ }
50
+
51
+ /**
52
+ * {@inheritdoc}
53
+ */
54
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
55
+ {
56
+ if ($this->replace || '' === $request->getUri()->getHost()) {
57
+ $uri = $request->getUri()
58
+ ->withHost($this->host->getHost())
59
+ ->withScheme($this->host->getScheme())
60
+ ->withPort($this->host->getPort())
61
+ ;
62
+
63
+ $request = $request->withUri($uri);
64
+ }
65
+
66
+ return $next($request);
67
+ }
68
+
69
+ private function configureOptions(OptionsResolver $resolver): void
70
+ {
71
+ $resolver->setDefaults([
72
+ 'replace' => false,
73
+ ]);
74
+ $resolver->setAllowedTypes('replace', 'bool');
75
+ }
76
+ }
src/vendor/php-http/client-common/src/Plugin/AddPathPlugin.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+ use Psr\Http\Message\UriInterface;
11
+
12
+ /**
13
+ * Prepend a base path to the request URI. Useful for base API URLs like http://domain.com/api.
14
+ *
15
+ * @author Sullivan Senechal <soullivaneuh@gmail.com>
16
+ */
17
+ final class AddPathPlugin implements Plugin
18
+ {
19
+ /**
20
+ * @var UriInterface
21
+ */
22
+ private $uri;
23
+
24
+ public function __construct(UriInterface $uri)
25
+ {
26
+ if ('' === $uri->getPath()) {
27
+ throw new \LogicException('URI path cannot be empty');
28
+ }
29
+
30
+ if ('/' === substr($uri->getPath(), -1)) {
31
+ $uri = $uri->withPath(rtrim($uri->getPath(), '/'));
32
+ }
33
+
34
+ $this->uri = $uri;
35
+ }
36
+
37
+ /**
38
+ * Adds a prefix in the beginning of the URL's path.
39
+ *
40
+ * The prefix is not added if that prefix is already on the URL's path. This will fail on the edge
41
+ * case of the prefix being repeated, for example if `https://example.com/api/api/foo` is a valid
42
+ * URL on the server and the configured prefix is `/api`.
43
+ *
44
+ * We looked at other solutions, but they are all much more complicated, while still having edge
45
+ * cases:
46
+ * - Doing an spl_object_hash on `$first` will lead to collisions over time because over time the
47
+ * hash can collide.
48
+ * - Have the PluginClient provide a magic header to identify the request chain and only apply
49
+ * this plugin once.
50
+ *
51
+ * There are 2 reasons for the AddPathPlugin to be executed twice on the same request:
52
+ * - A plugin can restart the chain by calling `$first`, e.g. redirect
53
+ * - A plugin can call `$next` more than once, e.g. retry
54
+ *
55
+ * Depending on the scenario, the path should or should not be added. E.g. `$first` could
56
+ * be called after a redirect response from the server. The server likely already has the
57
+ * correct path.
58
+ *
59
+ * No solution fits all use cases. This implementation will work fine for the common use cases.
60
+ * If you have a specific situation where this is not the right thing, you can build a custom plugin
61
+ * that does exactly what you need.
62
+ *
63
+ * {@inheritdoc}
64
+ */
65
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
66
+ {
67
+ $prepend = $this->uri->getPath();
68
+ $path = $request->getUri()->getPath();
69
+
70
+ if (substr($path, 0, strlen($prepend)) !== $prepend) {
71
+ $request = $request->withUri($request->getUri()
72
+ ->withPath($prepend.$path)
73
+ );
74
+ }
75
+
76
+ return $next($request);
77
+ }
78
+ }
src/vendor/php-http/client-common/src/Plugin/AuthenticationPlugin.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Message\Authentication;
9
+ use Http\Promise\Promise;
10
+ use Psr\Http\Message\RequestInterface;
11
+
12
+ /**
13
+ * Send an authenticated request.
14
+ *
15
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
16
+ */
17
+ final class AuthenticationPlugin implements Plugin
18
+ {
19
+ /**
20
+ * @var Authentication An authentication system
21
+ */
22
+ private $authentication;
23
+
24
+ public function __construct(Authentication $authentication)
25
+ {
26
+ $this->authentication = $authentication;
27
+ }
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
33
+ {
34
+ $request = $this->authentication->authenticate($request);
35
+
36
+ return $next($request);
37
+ }
38
+ }
src/vendor/php-http/client-common/src/Plugin/BaseUriPlugin.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+ use Psr\Http\Message\UriInterface;
11
+
12
+ /**
13
+ * Combines the AddHostPlugin and AddPathPlugin.
14
+ *
15
+ * @author Sullivan Senechal <soullivaneuh@gmail.com>
16
+ */
17
+ final class BaseUriPlugin implements Plugin
18
+ {
19
+ /**
20
+ * @var AddHostPlugin
21
+ */
22
+ private $addHostPlugin;
23
+
24
+ /**
25
+ * @var AddPathPlugin|null
26
+ */
27
+ private $addPathPlugin = null;
28
+
29
+ /**
30
+ * @param UriInterface $uri Has to contain a host name and can have a path
31
+ * @param array $hostConfig Config for AddHostPlugin. @see AddHostPlugin::configureOptions
32
+ */
33
+ public function __construct(UriInterface $uri, array $hostConfig = [])
34
+ {
35
+ $this->addHostPlugin = new AddHostPlugin($uri, $hostConfig);
36
+
37
+ if (rtrim($uri->getPath(), '/')) {
38
+ $this->addPathPlugin = new AddPathPlugin($uri);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * {@inheritdoc}
44
+ */
45
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
46
+ {
47
+ $addHostNext = function (RequestInterface $request) use ($next, $first) {
48
+ return $this->addHostPlugin->handleRequest($request, $next, $first);
49
+ };
50
+
51
+ if ($this->addPathPlugin) {
52
+ return $this->addPathPlugin->handleRequest($request, $addHostNext, $first);
53
+ }
54
+
55
+ return $addHostNext($request);
56
+ }
57
+ }
src/vendor/php-http/client-common/src/Plugin/ContentLengthPlugin.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Message\Encoding\ChunkStream;
9
+ use Http\Promise\Promise;
10
+ use Psr\Http\Message\RequestInterface;
11
+
12
+ /**
13
+ * Allow to set the correct content length header on the request or to transfer it as a chunk if not possible.
14
+ *
15
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
16
+ */
17
+ final class ContentLengthPlugin implements Plugin
18
+ {
19
+ /**
20
+ * {@inheritdoc}
21
+ */
22
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
23
+ {
24
+ if (!$request->hasHeader('Content-Length')) {
25
+ $stream = $request->getBody();
26
+
27
+ // Cannot determine the size so we use a chunk stream
28
+ if (null === $stream->getSize()) {
29
+ $stream = new ChunkStream($stream);
30
+ $request = $request->withBody($stream);
31
+ $request = $request->withAddedHeader('Transfer-Encoding', 'chunked');
32
+ } else {
33
+ $request = $request->withHeader('Content-Length', (string) $stream->getSize());
34
+ }
35
+ }
36
+
37
+ return $next($request);
38
+ }
39
+ }
src/vendor/php-http/client-common/src/Plugin/ContentTypePlugin.php ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+ use Psr\Http\Message\StreamInterface;
11
+ use Symfony\Component\OptionsResolver\OptionsResolver;
12
+
13
+ /**
14
+ * Allow to set the correct content type header on the request automatically only if it is not set.
15
+ *
16
+ * @author Karim Pinchon <karim.pinchon@gmail.com>
17
+ */
18
+ final class ContentTypePlugin implements Plugin
19
+ {
20
+ /**
21
+ * Allow to disable the content type detection when stream is too large (as it can consume a lot of resource).
22
+ *
23
+ * @var bool
24
+ *
25
+ * true skip the content type detection
26
+ * false detect the content type (default value)
27
+ */
28
+ private $skipDetection;
29
+
30
+ /**
31
+ * Determine the size stream limit for which the detection as to be skipped (default to 16Mb).
32
+ *
33
+ * @var int
34
+ */
35
+ private $sizeLimit;
36
+
37
+ /**
38
+ * @param array{'skip_detection'?: bool, 'size_limit'?: int} $config
39
+ *
40
+ * Configuration options:
41
+ * - skip_detection: true skip detection if stream size is bigger than $size_limit
42
+ * - size_limit: size stream limit for which the detection as to be skipped.
43
+ */
44
+ public function __construct(array $config = [])
45
+ {
46
+ $resolver = new OptionsResolver();
47
+ $resolver->setDefaults([
48
+ 'skip_detection' => false,
49
+ 'size_limit' => 16000000,
50
+ ]);
51
+ $resolver->setAllowedTypes('skip_detection', 'bool');
52
+ $resolver->setAllowedTypes('size_limit', 'int');
53
+
54
+ $options = $resolver->resolve($config);
55
+
56
+ $this->skipDetection = $options['skip_detection'];
57
+ $this->sizeLimit = $options['size_limit'];
58
+ }
59
+
60
+ /**
61
+ * {@inheritdoc}
62
+ */
63
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
64
+ {
65
+ if (!$request->hasHeader('Content-Type')) {
66
+ $stream = $request->getBody();
67
+ $streamSize = $stream->getSize();
68
+
69
+ if (!$stream->isSeekable()) {
70
+ return $next($request);
71
+ }
72
+
73
+ if (0 === $streamSize) {
74
+ return $next($request);
75
+ }
76
+
77
+ if ($this->skipDetection && (null === $streamSize || $streamSize >= $this->sizeLimit)) {
78
+ return $next($request);
79
+ }
80
+
81
+ if ($this->isJson($stream)) {
82
+ $request = $request->withHeader('Content-Type', 'application/json');
83
+
84
+ return $next($request);
85
+ }
86
+
87
+ if ($this->isXml($stream)) {
88
+ $request = $request->withHeader('Content-Type', 'application/xml');
89
+
90
+ return $next($request);
91
+ }
92
+ }
93
+
94
+ return $next($request);
95
+ }
96
+
97
+ private function isJson(StreamInterface $stream): bool
98
+ {
99
+ if (!function_exists('json_decode')) {
100
+ return false;
101
+ }
102
+ $stream->rewind();
103
+
104
+ json_decode($stream->getContents());
105
+
106
+ return JSON_ERROR_NONE === json_last_error();
107
+ }
108
+
109
+ private function isXml(StreamInterface $stream): bool
110
+ {
111
+ if (!function_exists('simplexml_load_string')) {
112
+ return false;
113
+ }
114
+ $stream->rewind();
115
+
116
+ $previousValue = libxml_use_internal_errors(true);
117
+ $isXml = simplexml_load_string($stream->getContents());
118
+ libxml_use_internal_errors($previousValue);
119
+
120
+ return false !== $isXml;
121
+ }
122
+ }
src/vendor/php-http/client-common/src/Plugin/CookiePlugin.php ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Client\Exception\TransferException;
9
+ use Http\Message\Cookie;
10
+ use Http\Message\CookieJar;
11
+ use Http\Message\CookieUtil;
12
+ use Http\Message\Exception\UnexpectedValueException;
13
+ use Http\Promise\Promise;
14
+ use Psr\Http\Message\RequestInterface;
15
+ use Psr\Http\Message\ResponseInterface;
16
+
17
+ /**
18
+ * Handle request cookies.
19
+ *
20
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
21
+ */
22
+ final class CookiePlugin implements Plugin
23
+ {
24
+ /**
25
+ * Cookie storage.
26
+ *
27
+ * @var CookieJar
28
+ */
29
+ private $cookieJar;
30
+
31
+ public function __construct(CookieJar $cookieJar)
32
+ {
33
+ $this->cookieJar = $cookieJar;
34
+ }
35
+
36
+ /**
37
+ * {@inheritdoc}
38
+ */
39
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
40
+ {
41
+ $cookies = [];
42
+ foreach ($this->cookieJar->getCookies() as $cookie) {
43
+ if ($cookie->isExpired()) {
44
+ continue;
45
+ }
46
+
47
+ if (!$cookie->matchDomain($request->getUri()->getHost())) {
48
+ continue;
49
+ }
50
+
51
+ if (!$cookie->matchPath($request->getUri()->getPath())) {
52
+ continue;
53
+ }
54
+
55
+ if ($cookie->isSecure() && ('https' !== $request->getUri()->getScheme())) {
56
+ continue;
57
+ }
58
+
59
+ $cookies[] = sprintf('%s=%s', $cookie->getName(), $cookie->getValue());
60
+ }
61
+
62
+ if (!empty($cookies)) {
63
+ $request = $request->withAddedHeader('Cookie', implode('; ', array_unique($cookies)));
64
+ }
65
+
66
+ return $next($request)->then(function (ResponseInterface $response) use ($request) {
67
+ if ($response->hasHeader('Set-Cookie')) {
68
+ $setCookies = $response->getHeader('Set-Cookie');
69
+
70
+ foreach ($setCookies as $setCookie) {
71
+ $cookie = $this->createCookie($request, $setCookie);
72
+
73
+ // Cookie invalid do not use it
74
+ if (null === $cookie) {
75
+ continue;
76
+ }
77
+
78
+ // Restrict setting cookie from another domain
79
+ if (!preg_match("/\.{$cookie->getDomain()}$/", '.'.$request->getUri()->getHost())) {
80
+ continue;
81
+ }
82
+
83
+ $this->cookieJar->addCookie($cookie);
84
+ }
85
+ }
86
+
87
+ return $response;
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Creates a cookie from a string.
93
+ *
94
+ * @throws TransferException
95
+ */
96
+ private function createCookie(RequestInterface $request, string $setCookieHeader): ?Cookie
97
+ {
98
+ $parts = array_map('trim', explode(';', $setCookieHeader));
99
+
100
+ if ('' === $parts[0] || false === strpos($parts[0], '=')) {
101
+ return null;
102
+ }
103
+
104
+ list($name, $cookieValue) = $this->createValueKey(array_shift($parts));
105
+
106
+ $maxAge = null;
107
+ $expires = null;
108
+ $domain = $request->getUri()->getHost();
109
+ $path = $request->getUri()->getPath();
110
+ $secure = false;
111
+ $httpOnly = false;
112
+
113
+ // Add the cookie pieces into the parsed data array
114
+ foreach ($parts as $part) {
115
+ list($key, $value) = $this->createValueKey($part);
116
+
117
+ switch (strtolower($key)) {
118
+ case 'expires':
119
+ try {
120
+ $expires = CookieUtil::parseDate((string) $value);
121
+ } catch (UnexpectedValueException $e) {
122
+ throw new TransferException(
123
+ sprintf(
124
+ 'Cookie header `%s` expires value `%s` could not be converted to date',
125
+ $name,
126
+ $value
127
+ ),
128
+ 0,
129
+ $e
130
+ );
131
+ }
132
+
133
+ break;
134
+
135
+ case 'max-age':
136
+ $maxAge = (int) $value;
137
+
138
+ break;
139
+
140
+ case 'domain':
141
+ $domain = $value;
142
+
143
+ break;
144
+
145
+ case 'path':
146
+ $path = $value;
147
+
148
+ break;
149
+
150
+ case 'secure':
151
+ $secure = true;
152
+
153
+ break;
154
+
155
+ case 'httponly':
156
+ $httpOnly = true;
157
+
158
+ break;
159
+ }
160
+ }
161
+
162
+ return new Cookie($name, $cookieValue, $maxAge, $domain, $path, $secure, $httpOnly, $expires);
163
+ }
164
+
165
+ /**
166
+ * Separates key/value pair from cookie.
167
+ *
168
+ * @param string $part A single cookie value in format key=value
169
+ *
170
+ * @return array{0:string, 1:?string}
171
+ */
172
+ private function createValueKey(string $part): array
173
+ {
174
+ $parts = explode('=', $part, 2);
175
+ $key = trim($parts[0]);
176
+ $value = isset($parts[1]) ? trim($parts[1]) : null;
177
+
178
+ return [$key, $value];
179
+ }
180
+ }
src/vendor/php-http/client-common/src/Plugin/DecoderPlugin.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Message\Encoding;
9
+ use Http\Promise\Promise;
10
+ use Psr\Http\Message\RequestInterface;
11
+ use Psr\Http\Message\ResponseInterface;
12
+ use Psr\Http\Message\StreamInterface;
13
+ use Symfony\Component\OptionsResolver\OptionsResolver;
14
+
15
+ /**
16
+ * Allow to decode response body with a chunk, deflate, compress or gzip encoding.
17
+ *
18
+ * If zlib is not installed, only chunked encoding can be handled.
19
+ *
20
+ * If Content-Encoding is not disabled, the plugin will add an Accept-Encoding header for the encoding methods it supports.
21
+ *
22
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
23
+ */
24
+ final class DecoderPlugin implements Plugin
25
+ {
26
+ /**
27
+ * @var bool Whether this plugin decode stream with value in the Content-Encoding header (default to true).
28
+ *
29
+ * If set to false only the Transfer-Encoding header will be used
30
+ */
31
+ private $useContentEncoding;
32
+
33
+ /**
34
+ * @param array{'use_content_encoding'?: bool} $config
35
+ *
36
+ * Configuration options:
37
+ * - use_content_encoding: Whether this plugin should look at the Content-Encoding header first or only at the Transfer-Encoding (defaults to true).
38
+ */
39
+ public function __construct(array $config = [])
40
+ {
41
+ $resolver = new OptionsResolver();
42
+ $resolver->setDefaults([
43
+ 'use_content_encoding' => true,
44
+ ]);
45
+ $resolver->setAllowedTypes('use_content_encoding', 'bool');
46
+ $options = $resolver->resolve($config);
47
+
48
+ $this->useContentEncoding = $options['use_content_encoding'];
49
+ }
50
+
51
+ /**
52
+ * {@inheritdoc}
53
+ */
54
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
55
+ {
56
+ $encodings = extension_loaded('zlib') ? ['gzip', 'deflate'] : ['identity'];
57
+
58
+ if ($this->useContentEncoding) {
59
+ $request = $request->withHeader('Accept-Encoding', $encodings);
60
+ }
61
+ $encodings[] = 'chunked';
62
+ $request = $request->withHeader('TE', $encodings);
63
+
64
+ return $next($request)->then(function (ResponseInterface $response) {
65
+ return $this->decodeResponse($response);
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Decode a response body given its Transfer-Encoding or Content-Encoding value.
71
+ */
72
+ private function decodeResponse(ResponseInterface $response): ResponseInterface
73
+ {
74
+ $response = $this->decodeOnEncodingHeader('Transfer-Encoding', $response);
75
+
76
+ if ($this->useContentEncoding) {
77
+ $response = $this->decodeOnEncodingHeader('Content-Encoding', $response);
78
+ }
79
+
80
+ return $response;
81
+ }
82
+
83
+ /**
84
+ * Decode a response on a specific header (content encoding or transfer encoding mainly).
85
+ */
86
+ private function decodeOnEncodingHeader(string $headerName, ResponseInterface $response): ResponseInterface
87
+ {
88
+ if ($response->hasHeader($headerName)) {
89
+ $encodings = $response->getHeader($headerName);
90
+ $newEncodings = [];
91
+
92
+ while ($encoding = array_pop($encodings)) {
93
+ $stream = $this->decorateStream($encoding, $response->getBody());
94
+
95
+ if (false === $stream) {
96
+ array_unshift($newEncodings, $encoding);
97
+
98
+ continue;
99
+ }
100
+
101
+ $response = $response->withBody($stream);
102
+ }
103
+
104
+ if (\count($newEncodings) > 0) {
105
+ $response = $response->withHeader($headerName, $newEncodings);
106
+ } else {
107
+ $response = $response->withoutHeader($headerName);
108
+ }
109
+ }
110
+
111
+ return $response;
112
+ }
113
+
114
+ /**
115
+ * Decorate a stream given an encoding.
116
+ *
117
+ * @return StreamInterface|false A new stream interface or false if encoding is not supported
118
+ */
119
+ private function decorateStream(string $encoding, StreamInterface $stream)
120
+ {
121
+ if ('chunked' === strtolower($encoding)) {
122
+ return new Encoding\DechunkStream($stream);
123
+ }
124
+
125
+ if ('deflate' === strtolower($encoding)) {
126
+ return new Encoding\DecompressStream($stream);
127
+ }
128
+
129
+ if ('gzip' === strtolower($encoding)) {
130
+ return new Encoding\GzipDecodeStream($stream);
131
+ }
132
+
133
+ return false;
134
+ }
135
+ }
src/vendor/php-http/client-common/src/Plugin/ErrorPlugin.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Exception\ClientErrorException;
8
+ use Http\Client\Common\Exception\ServerErrorException;
9
+ use Http\Client\Common\Plugin;
10
+ use Http\Promise\Promise;
11
+ use Psr\Http\Message\RequestInterface;
12
+ use Psr\Http\Message\ResponseInterface;
13
+ use Symfony\Component\OptionsResolver\OptionsResolver;
14
+
15
+ /**
16
+ * Throw exception when the response of a request is not acceptable.
17
+ *
18
+ * Status codes 400-499 lead to a ClientErrorException, status 500-599 to a ServerErrorException.
19
+ *
20
+ * Warning
21
+ * =======
22
+ *
23
+ * Throwing an exception on a valid response violates the PSR-18 specification.
24
+ * This plugin is provided as a convenience when writing a small application.
25
+ * When providing a client to a third party library, this plugin must not be
26
+ * included, or the third party library will have problems with error handling.
27
+ *
28
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
29
+ */
30
+ final class ErrorPlugin implements Plugin
31
+ {
32
+ /**
33
+ * @var bool Whether this plugin should only throw 5XX Exceptions (default to false).
34
+ *
35
+ * If set to true 4XX Responses code will never throw an exception
36
+ */
37
+ private $onlyServerException;
38
+
39
+ /**
40
+ * @param array{'only_server_exception'?: bool} $config
41
+ *
42
+ * Configuration options:
43
+ * - only_server_exception: Whether this plugin should only throw 5XX Exceptions (default to false).
44
+ */
45
+ public function __construct(array $config = [])
46
+ {
47
+ $resolver = new OptionsResolver();
48
+ $resolver->setDefaults([
49
+ 'only_server_exception' => false,
50
+ ]);
51
+ $resolver->setAllowedTypes('only_server_exception', 'bool');
52
+ $options = $resolver->resolve($config);
53
+
54
+ $this->onlyServerException = $options['only_server_exception'];
55
+ }
56
+
57
+ /**
58
+ * {@inheritdoc}
59
+ */
60
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
61
+ {
62
+ $promise = $next($request);
63
+
64
+ return $promise->then(function (ResponseInterface $response) use ($request) {
65
+ return $this->transformResponseToException($request, $response);
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Transform response to an error if possible.
71
+ *
72
+ * @param RequestInterface $request Request of the call
73
+ * @param ResponseInterface $response Response of the call
74
+ *
75
+ * @throws ClientErrorException If response status code is a 4xx
76
+ * @throws ServerErrorException If response status code is a 5xx
77
+ *
78
+ * @return ResponseInterface If status code is not in 4xx or 5xx return response
79
+ */
80
+ private function transformResponseToException(RequestInterface $request, ResponseInterface $response): ResponseInterface
81
+ {
82
+ if (!$this->onlyServerException && $response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
83
+ throw new ClientErrorException($response->getReasonPhrase(), $request, $response);
84
+ }
85
+
86
+ if ($response->getStatusCode() >= 500 && $response->getStatusCode() < 600) {
87
+ throw new ServerErrorException($response->getReasonPhrase(), $request, $response);
88
+ }
89
+
90
+ return $response;
91
+ }
92
+ }
src/vendor/php-http/client-common/src/Plugin/HeaderAppendPlugin.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+
11
+ /**
12
+ * Append headers to the request.
13
+ *
14
+ * If the header already exists the value will be appended to the current value.
15
+ *
16
+ * This only makes sense for headers that can have multiple values like 'Forwarded'
17
+ *
18
+ * @see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
19
+ *
20
+ * @author Soufiane Ghzal <sghzal@gmail.com>
21
+ */
22
+ final class HeaderAppendPlugin implements Plugin
23
+ {
24
+ /**
25
+ * @var array
26
+ */
27
+ private $headers;
28
+
29
+ /**
30
+ * @param array $headers Hashmap of header name to header value
31
+ */
32
+ public function __construct(array $headers)
33
+ {
34
+ $this->headers = $headers;
35
+ }
36
+
37
+ /**
38
+ * {@inheritdoc}
39
+ */
40
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
41
+ {
42
+ foreach ($this->headers as $header => $headerValue) {
43
+ $request = $request->withAddedHeader($header, $headerValue);
44
+ }
45
+
46
+ return $next($request);
47
+ }
48
+ }
src/vendor/php-http/client-common/src/Plugin/HeaderDefaultsPlugin.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+
11
+ /**
12
+ * Set header to default value if it does not exist.
13
+ *
14
+ * If a given header already exists the value wont be replaced and the request wont be changed.
15
+ *
16
+ * @author Soufiane Ghzal <sghzal@gmail.com>
17
+ */
18
+ final class HeaderDefaultsPlugin implements Plugin
19
+ {
20
+ /**
21
+ * @var array
22
+ */
23
+ private $headers = [];
24
+
25
+ /**
26
+ * @param array $headers Hashmap of header name to header value
27
+ */
28
+ public function __construct(array $headers)
29
+ {
30
+ $this->headers = $headers;
31
+ }
32
+
33
+ /**
34
+ * {@inheritdoc}
35
+ */
36
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
37
+ {
38
+ foreach ($this->headers as $header => $headerValue) {
39
+ if (!$request->hasHeader($header)) {
40
+ $request = $request->withHeader($header, $headerValue);
41
+ }
42
+ }
43
+
44
+ return $next($request);
45
+ }
46
+ }
src/vendor/php-http/client-common/src/Plugin/HeaderRemovePlugin.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+
11
+ /**
12
+ * Removes headers from the request.
13
+ *
14
+ * @author Soufiane Ghzal <sghzal@gmail.com>
15
+ */
16
+ final class HeaderRemovePlugin implements Plugin
17
+ {
18
+ /**
19
+ * @var array
20
+ */
21
+ private $headers = [];
22
+
23
+ /**
24
+ * @param array $headers List of header names to remove from the request
25
+ */
26
+ public function __construct(array $headers)
27
+ {
28
+ $this->headers = $headers;
29
+ }
30
+
31
+ /**
32
+ * {@inheritdoc}
33
+ */
34
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
35
+ {
36
+ foreach ($this->headers as $header) {
37
+ if ($request->hasHeader($header)) {
38
+ $request = $request->withoutHeader($header);
39
+ }
40
+ }
41
+
42
+ return $next($request);
43
+ }
44
+ }
src/vendor/php-http/client-common/src/Plugin/HeaderSetPlugin.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+
11
+ /**
12
+ * Set headers on the request.
13
+ *
14
+ * If the header does not exist it wil be set, if the header already exists it will be replaced.
15
+ *
16
+ * @author Soufiane Ghzal <sghzal@gmail.com>
17
+ */
18
+ final class HeaderSetPlugin implements Plugin
19
+ {
20
+ /**
21
+ * @var array
22
+ */
23
+ private $headers;
24
+
25
+ /**
26
+ * @param array $headers Hashmap of header name to header value
27
+ */
28
+ public function __construct(array $headers)
29
+ {
30
+ $this->headers = $headers;
31
+ }
32
+
33
+ /**
34
+ * {@inheritdoc}
35
+ */
36
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
37
+ {
38
+ foreach ($this->headers as $header => $headerValue) {
39
+ $request = $request->withHeader($header, $headerValue);
40
+ }
41
+
42
+ return $next($request);
43
+ }
44
+ }
src/vendor/php-http/client-common/src/Plugin/HistoryPlugin.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Client\ClientExceptionInterface;
10
+ use Psr\Http\Message\RequestInterface;
11
+ use Psr\Http\Message\ResponseInterface;
12
+
13
+ /**
14
+ * Record HTTP calls.
15
+ *
16
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
17
+ */
18
+ final class HistoryPlugin implements Plugin
19
+ {
20
+ /**
21
+ * Journal use to store request / responses / exception.
22
+ *
23
+ * @var Journal
24
+ */
25
+ private $journal;
26
+
27
+ public function __construct(Journal $journal)
28
+ {
29
+ $this->journal = $journal;
30
+ }
31
+
32
+ /**
33
+ * {@inheritdoc}
34
+ */
35
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
36
+ {
37
+ $journal = $this->journal;
38
+
39
+ return $next($request)->then(function (ResponseInterface $response) use ($request, $journal) {
40
+ $journal->addSuccess($request, $response);
41
+
42
+ return $response;
43
+ }, function (ClientExceptionInterface $exception) use ($request, $journal) {
44
+ $journal->addFailure($request, $exception);
45
+
46
+ throw $exception;
47
+ });
48
+ }
49
+ }
src/vendor/php-http/client-common/src/Plugin/Journal.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Psr\Http\Client\ClientExceptionInterface;
8
+ use Psr\Http\Message\RequestInterface;
9
+ use Psr\Http\Message\ResponseInterface;
10
+
11
+ /**
12
+ * Records history of HTTP calls.
13
+ *
14
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
15
+ */
16
+ interface Journal
17
+ {
18
+ /**
19
+ * Record a successful call.
20
+ *
21
+ * @param RequestInterface $request Request use to make the call
22
+ * @param ResponseInterface $response Response returned by the call
23
+ */
24
+ public function addSuccess(RequestInterface $request, ResponseInterface $response);
25
+
26
+ /**
27
+ * Record a failed call.
28
+ *
29
+ * @param RequestInterface $request Request use to make the call
30
+ * @param ClientExceptionInterface $exception Exception returned by the call
31
+ */
32
+ public function addFailure(RequestInterface $request, ClientExceptionInterface $exception);
33
+ }
src/vendor/php-http/client-common/src/Plugin/QueryDefaultsPlugin.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+
11
+ /**
12
+ * Set query to default value if it does not exist.
13
+ *
14
+ * If a given query parameter already exists the value wont be replaced and the request wont be changed.
15
+ *
16
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
17
+ */
18
+ final class QueryDefaultsPlugin implements Plugin
19
+ {
20
+ /**
21
+ * @var array
22
+ */
23
+ private $queryParams = [];
24
+
25
+ /**
26
+ * @param array $queryParams Hashmap of query name to query value. Names and values must not be url encoded as
27
+ * this plugin will encode them
28
+ */
29
+ public function __construct(array $queryParams)
30
+ {
31
+ $this->queryParams = $queryParams;
32
+ }
33
+
34
+ /**
35
+ * {@inheritdoc}
36
+ */
37
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
38
+ {
39
+ $uri = $request->getUri();
40
+
41
+ parse_str($uri->getQuery(), $query);
42
+ $query += $this->queryParams;
43
+
44
+ $request = $request->withUri(
45
+ $uri->withQuery(http_build_query($query))
46
+ );
47
+
48
+ return $next($request);
49
+ }
50
+ }
src/vendor/php-http/client-common/src/Plugin/RedirectPlugin.php ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Exception\CircularRedirectionException;
8
+ use Http\Client\Common\Exception\MultipleRedirectionException;
9
+ use Http\Client\Common\Plugin;
10
+ use Http\Client\Exception\HttpException;
11
+ use Http\Promise\Promise;
12
+ use Psr\Http\Message\RequestInterface;
13
+ use Psr\Http\Message\ResponseInterface;
14
+ use Psr\Http\Message\UriInterface;
15
+ use Symfony\Component\OptionsResolver\OptionsResolver;
16
+
17
+ /**
18
+ * Follow redirections.
19
+ *
20
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
21
+ */
22
+ final class RedirectPlugin implements Plugin
23
+ {
24
+ /**
25
+ * Rule on how to redirect, change method for the new request.
26
+ *
27
+ * @var array
28
+ */
29
+ private $redirectCodes = [
30
+ 300 => [
31
+ 'switch' => [
32
+ 'unless' => ['GET', 'HEAD'],
33
+ 'to' => 'GET',
34
+ ],
35
+ 'multiple' => true,
36
+ 'permanent' => false,
37
+ ],
38
+ 301 => [
39
+ 'switch' => [
40
+ 'unless' => ['GET', 'HEAD'],
41
+ 'to' => 'GET',
42
+ ],
43
+ 'multiple' => false,
44
+ 'permanent' => true,
45
+ ],
46
+ 302 => [
47
+ 'switch' => [
48
+ 'unless' => ['GET', 'HEAD'],
49
+ 'to' => 'GET',
50
+ ],
51
+ 'multiple' => false,
52
+ 'permanent' => false,
53
+ ],
54
+ 303 => [
55
+ 'switch' => [
56
+ 'unless' => ['GET', 'HEAD'],
57
+ 'to' => 'GET',
58
+ ],
59
+ 'multiple' => false,
60
+ 'permanent' => false,
61
+ ],
62
+ 307 => [
63
+ 'switch' => false,
64
+ 'multiple' => false,
65
+ 'permanent' => false,
66
+ ],
67
+ 308 => [
68
+ 'switch' => false,
69
+ 'multiple' => false,
70
+ 'permanent' => true,
71
+ ],
72
+ ];
73
+
74
+ /**
75
+ * Determine how header should be preserved from old request.
76
+ *
77
+ * @var bool|array
78
+ *
79
+ * true will keep all previous headers (default value)
80
+ * false will ditch all previous headers
81
+ * string[] will keep only headers with the specified names
82
+ */
83
+ private $preserveHeader;
84
+
85
+ /**
86
+ * Store all previous redirect from 301 / 308 status code.
87
+ *
88
+ * @var array
89
+ */
90
+ private $redirectStorage = [];
91
+
92
+ /**
93
+ * Whether the location header must be directly used for a multiple redirection status code (300).
94
+ *
95
+ * @var bool
96
+ */
97
+ private $useDefaultForMultiple;
98
+
99
+ /**
100
+ * @var string[][] Chain identifier => list of URLs for this chain
101
+ */
102
+ private $circularDetection = [];
103
+
104
+ /**
105
+ * @param array{'preserve_header'?: bool|string[], 'use_default_for_multiple'?: bool, 'strict'?: bool} $config
106
+ *
107
+ * Configuration options:
108
+ * - preserve_header: True keeps all headers, false remove all of them, an array is interpreted as a list of header names to keep
109
+ * - use_default_for_multiple: Whether the location header must be directly used for a multiple redirection status code (300)
110
+ * - strict: When true, redirect codes 300, 301, 302 will not modify request method and body.
111
+ */
112
+ public function __construct(array $config = [])
113
+ {
114
+ $resolver = new OptionsResolver();
115
+ $resolver->setDefaults([
116
+ 'preserve_header' => true,
117
+ 'use_default_for_multiple' => true,
118
+ 'strict' => false,
119
+ ]);
120
+ $resolver->setAllowedTypes('preserve_header', ['bool', 'array']);
121
+ $resolver->setAllowedTypes('use_default_for_multiple', 'bool');
122
+ $resolver->setAllowedTypes('strict', 'bool');
123
+ $resolver->setNormalizer('preserve_header', function (OptionsResolver $resolver, $value) {
124
+ if (is_bool($value) && false === $value) {
125
+ return [];
126
+ }
127
+
128
+ return $value;
129
+ });
130
+ $options = $resolver->resolve($config);
131
+
132
+ $this->preserveHeader = $options['preserve_header'];
133
+ $this->useDefaultForMultiple = $options['use_default_for_multiple'];
134
+
135
+ if ($options['strict']) {
136
+ $this->redirectCodes[300]['switch'] = false;
137
+ $this->redirectCodes[301]['switch'] = false;
138
+ $this->redirectCodes[302]['switch'] = false;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * {@inheritdoc}
144
+ */
145
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
146
+ {
147
+ // Check in storage
148
+ if (array_key_exists((string) $request->getUri(), $this->redirectStorage)) {
149
+ $uri = $this->redirectStorage[(string) $request->getUri()]['uri'];
150
+ $statusCode = $this->redirectStorage[(string) $request->getUri()]['status'];
151
+ $redirectRequest = $this->buildRedirectRequest($request, $uri, $statusCode);
152
+
153
+ return $first($redirectRequest);
154
+ }
155
+
156
+ return $next($request)->then(function (ResponseInterface $response) use ($request, $first): ResponseInterface {
157
+ $statusCode = $response->getStatusCode();
158
+
159
+ if (!array_key_exists($statusCode, $this->redirectCodes)) {
160
+ return $response;
161
+ }
162
+
163
+ $uri = $this->createUri($response, $request);
164
+ $redirectRequest = $this->buildRedirectRequest($request, $uri, $statusCode);
165
+ $chainIdentifier = spl_object_hash((object) $first);
166
+
167
+ if (!array_key_exists($chainIdentifier, $this->circularDetection)) {
168
+ $this->circularDetection[$chainIdentifier] = [];
169
+ }
170
+
171
+ $this->circularDetection[$chainIdentifier][] = (string) $request->getUri();
172
+
173
+ if (in_array((string) $redirectRequest->getUri(), $this->circularDetection[$chainIdentifier])) {
174
+ throw new CircularRedirectionException('Circular redirection detected', $request, $response);
175
+ }
176
+
177
+ if ($this->redirectCodes[$statusCode]['permanent']) {
178
+ $this->redirectStorage[(string) $request->getUri()] = [
179
+ 'uri' => $uri,
180
+ 'status' => $statusCode,
181
+ ];
182
+ }
183
+
184
+ // Call redirect request synchronously
185
+ return $first($redirectRequest)->wait();
186
+ });
187
+ }
188
+
189
+ private function buildRedirectRequest(RequestInterface $originalRequest, UriInterface $targetUri, int $statusCode): RequestInterface
190
+ {
191
+ $originalRequest = $originalRequest->withUri($targetUri);
192
+
193
+ if (false !== $this->redirectCodes[$statusCode]['switch'] && !in_array($originalRequest->getMethod(), $this->redirectCodes[$statusCode]['switch']['unless'])) {
194
+ $originalRequest = $originalRequest->withMethod($this->redirectCodes[$statusCode]['switch']['to']);
195
+ }
196
+
197
+ if (is_array($this->preserveHeader)) {
198
+ $headers = array_keys($originalRequest->getHeaders());
199
+
200
+ foreach ($headers as $name) {
201
+ if (!in_array($name, $this->preserveHeader)) {
202
+ $originalRequest = $originalRequest->withoutHeader($name);
203
+ }
204
+ }
205
+ }
206
+
207
+ return $originalRequest;
208
+ }
209
+
210
+ /**
211
+ * Creates a new Uri from the old request and the location header.
212
+ *
213
+ * @throws HttpException If location header is not usable (missing or incorrect)
214
+ * @throws MultipleRedirectionException If a 300 status code is received and default location cannot be resolved (doesn't use the location header or not present)
215
+ */
216
+ private function createUri(ResponseInterface $redirectResponse, RequestInterface $originalRequest): UriInterface
217
+ {
218
+ if ($this->redirectCodes[$redirectResponse->getStatusCode()]['multiple'] && (!$this->useDefaultForMultiple || !$redirectResponse->hasHeader('Location'))) {
219
+ throw new MultipleRedirectionException('Cannot choose a redirection', $originalRequest, $redirectResponse);
220
+ }
221
+
222
+ if (!$redirectResponse->hasHeader('Location')) {
223
+ throw new HttpException('Redirect status code, but no location header present in the response', $originalRequest, $redirectResponse);
224
+ }
225
+
226
+ $location = $redirectResponse->getHeaderLine('Location');
227
+ $parsedLocation = parse_url($location);
228
+
229
+ if (false === $parsedLocation) {
230
+ throw new HttpException(sprintf('Location %s could not be parsed', $location), $originalRequest, $redirectResponse);
231
+ }
232
+
233
+ $uri = $originalRequest->getUri();
234
+
235
+ if (array_key_exists('scheme', $parsedLocation)) {
236
+ $uri = $uri->withScheme($parsedLocation['scheme']);
237
+ }
238
+
239
+ if (array_key_exists('host', $parsedLocation)) {
240
+ $uri = $uri->withHost($parsedLocation['host']);
241
+ }
242
+
243
+ if (array_key_exists('port', $parsedLocation)) {
244
+ $uri = $uri->withPort($parsedLocation['port']);
245
+ }
246
+
247
+ if (array_key_exists('path', $parsedLocation)) {
248
+ $uri = $uri->withPath($parsedLocation['path']);
249
+ }
250
+
251
+ if (array_key_exists('query', $parsedLocation)) {
252
+ $uri = $uri->withQuery($parsedLocation['query']);
253
+ } else {
254
+ $uri = $uri->withQuery('');
255
+ }
256
+
257
+ if (array_key_exists('fragment', $parsedLocation)) {
258
+ $uri = $uri->withFragment($parsedLocation['fragment']);
259
+ } else {
260
+ $uri = $uri->withFragment('');
261
+ }
262
+
263
+ return $uri;
264
+ }
265
+ }
src/vendor/php-http/client-common/src/Plugin/RequestMatcherPlugin.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Message\RequestMatcher;
9
+ use Http\Promise\Promise;
10
+ use Psr\Http\Message\RequestInterface;
11
+
12
+ /**
13
+ * Apply a delegated plugin based on a request match.
14
+ *
15
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
16
+ */
17
+ final class RequestMatcherPlugin implements Plugin
18
+ {
19
+ /**
20
+ * @var RequestMatcher
21
+ */
22
+ private $requestMatcher;
23
+
24
+ /**
25
+ * @var Plugin|null
26
+ */
27
+ private $successPlugin;
28
+
29
+ /**
30
+ * @var Plugin|null
31
+ */
32
+ private $failurePlugin;
33
+
34
+ public function __construct(RequestMatcher $requestMatcher, ?Plugin $delegateOnMatch, Plugin $delegateOnNoMatch = null)
35
+ {
36
+ $this->requestMatcher = $requestMatcher;
37
+ $this->successPlugin = $delegateOnMatch;
38
+ $this->failurePlugin = $delegateOnNoMatch;
39
+ }
40
+
41
+ /**
42
+ * {@inheritdoc}
43
+ */
44
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
45
+ {
46
+ if ($this->requestMatcher->matches($request)) {
47
+ if (null !== $this->successPlugin) {
48
+ return $this->successPlugin->handleRequest($request, $next, $first);
49
+ }
50
+ } elseif (null !== $this->failurePlugin) {
51
+ return $this->failurePlugin->handleRequest($request, $next, $first);
52
+ }
53
+
54
+ return $next($request);
55
+ }
56
+ }
src/vendor/php-http/client-common/src/Plugin/RequestSeekableBodyPlugin.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Message\Stream\BufferedStream;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+
11
+ /**
12
+ * Allow body used in request to be always seekable.
13
+ *
14
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
15
+ */
16
+ final class RequestSeekableBodyPlugin extends SeekableBodyPlugin
17
+ {
18
+ /**
19
+ * {@inheritdoc}
20
+ */
21
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
22
+ {
23
+ if (!$request->getBody()->isSeekable()) {
24
+ $request = $request->withBody(new BufferedStream($request->getBody(), $this->useFileBuffer, $this->memoryBufferSize));
25
+ }
26
+
27
+ return $next($request);
28
+ }
29
+ }
src/vendor/php-http/client-common/src/Plugin/ResponseSeekableBodyPlugin.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Message\Stream\BufferedStream;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+ use Psr\Http\Message\ResponseInterface;
11
+
12
+ /**
13
+ * Allow body used in response to be always seekable.
14
+ *
15
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
16
+ */
17
+ final class ResponseSeekableBodyPlugin extends SeekableBodyPlugin
18
+ {
19
+ /**
20
+ * {@inheritdoc}
21
+ */
22
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
23
+ {
24
+ return $next($request)->then(function (ResponseInterface $response) {
25
+ if ($response->getBody()->isSeekable()) {
26
+ return $response;
27
+ }
28
+
29
+ return $response->withBody(new BufferedStream($response->getBody(), $this->useFileBuffer, $this->memoryBufferSize));
30
+ });
31
+ }
32
+ }
src/vendor/php-http/client-common/src/Plugin/RetryPlugin.php ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Http\Client\Exception\HttpException;
9
+ use Http\Promise\Promise;
10
+ use Psr\Http\Client\ClientExceptionInterface;
11
+ use Psr\Http\Message\RequestInterface;
12
+ use Psr\Http\Message\ResponseInterface;
13
+ use Symfony\Component\OptionsResolver\OptionsResolver;
14
+
15
+ /**
16
+ * Retry the request if an exception is thrown.
17
+ *
18
+ * By default will retry only one time.
19
+ *
20
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
21
+ */
22
+ final class RetryPlugin implements Plugin
23
+ {
24
+ /**
25
+ * Number of retry before sending an exception.
26
+ *
27
+ * @var int
28
+ */
29
+ private $retry;
30
+
31
+ /**
32
+ * @var callable
33
+ */
34
+ private $errorResponseDelay;
35
+
36
+ /**
37
+ * @var callable
38
+ */
39
+ private $errorResponseDecider;
40
+
41
+ /**
42
+ * @var callable
43
+ */
44
+ private $exceptionDecider;
45
+
46
+ /**
47
+ * @var callable
48
+ */
49
+ private $exceptionDelay;
50
+
51
+ /**
52
+ * Store the retry counter for each request.
53
+ *
54
+ * @var array
55
+ */
56
+ private $retryStorage = [];
57
+
58
+ /**
59
+ * @param array{'retries'?: int, 'error_response_decider'?: callable, 'exception_decider'?: callable, 'error_response_delay'?: callable, 'exception_delay'?: callable} $config
60
+ *
61
+ * Configuration options:
62
+ * - retries: Number of retries to attempt if an exception occurs before letting the exception bubble up
63
+ * - error_response_decider: A callback that gets a request and response to decide whether the request should be retried
64
+ * - exception_decider: A callback that gets a request and an exception to decide after a failure whether the request should be retried
65
+ * - error_response_delay: A callback that gets a request and response and the current number of retries and returns how many microseconds we should wait before trying again
66
+ * - exception_delay: A callback that gets a request, an exception and the current number of retries and returns how many microseconds we should wait before trying again
67
+ */
68
+ public function __construct(array $config = [])
69
+ {
70
+ $resolver = new OptionsResolver();
71
+ $resolver->setDefaults([
72
+ 'retries' => 1,
73
+ 'error_response_decider' => function (RequestInterface $request, ResponseInterface $response) {
74
+ // do not retry client errors
75
+ return $response->getStatusCode() >= 500 && $response->getStatusCode() < 600;
76
+ },
77
+ 'exception_decider' => function (RequestInterface $request, ClientExceptionInterface $e) {
78
+ // do not retry client errors
79
+ return !$e instanceof HttpException || $e->getCode() >= 500 && $e->getCode() < 600;
80
+ },
81
+ 'error_response_delay' => __CLASS__.'::defaultErrorResponseDelay',
82
+ 'exception_delay' => __CLASS__.'::defaultExceptionDelay',
83
+ ]);
84
+
85
+ $resolver->setAllowedTypes('retries', 'int');
86
+ $resolver->setAllowedTypes('error_response_decider', 'callable');
87
+ $resolver->setAllowedTypes('exception_decider', 'callable');
88
+ $resolver->setAllowedTypes('error_response_delay', 'callable');
89
+ $resolver->setAllowedTypes('exception_delay', 'callable');
90
+ $options = $resolver->resolve($config);
91
+
92
+ $this->retry = $options['retries'];
93
+ $this->errorResponseDecider = $options['error_response_decider'];
94
+ $this->errorResponseDelay = $options['error_response_delay'];
95
+ $this->exceptionDecider = $options['exception_decider'];
96
+ $this->exceptionDelay = $options['exception_delay'];
97
+ }
98
+
99
+ /**
100
+ * {@inheritdoc}
101
+ */
102
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
103
+ {
104
+ $chainIdentifier = spl_object_hash((object) $first);
105
+
106
+ return $next($request)->then(function (ResponseInterface $response) use ($request, $next, $first, $chainIdentifier) {
107
+ if (!array_key_exists($chainIdentifier, $this->retryStorage)) {
108
+ $this->retryStorage[$chainIdentifier] = 0;
109
+ }
110
+
111
+ if ($this->retryStorage[$chainIdentifier] >= $this->retry) {
112
+ unset($this->retryStorage[$chainIdentifier]);
113
+
114
+ return $response;
115
+ }
116
+
117
+ if (call_user_func($this->errorResponseDecider, $request, $response)) {
118
+ /** @var int $time */
119
+ $time = call_user_func($this->errorResponseDelay, $request, $response, $this->retryStorage[$chainIdentifier]);
120
+ $response = $this->retry($request, $next, $first, $chainIdentifier, $time);
121
+ }
122
+
123
+ if (array_key_exists($chainIdentifier, $this->retryStorage)) {
124
+ unset($this->retryStorage[$chainIdentifier]);
125
+ }
126
+
127
+ return $response;
128
+ }, function (ClientExceptionInterface $exception) use ($request, $next, $first, $chainIdentifier) {
129
+ if (!array_key_exists($chainIdentifier, $this->retryStorage)) {
130
+ $this->retryStorage[$chainIdentifier] = 0;
131
+ }
132
+
133
+ if ($this->retryStorage[$chainIdentifier] >= $this->retry) {
134
+ unset($this->retryStorage[$chainIdentifier]);
135
+
136
+ throw $exception;
137
+ }
138
+
139
+ if (!call_user_func($this->exceptionDecider, $request, $exception)) {
140
+ throw $exception;
141
+ }
142
+
143
+ /** @var int $time */
144
+ $time = call_user_func($this->exceptionDelay, $request, $exception, $this->retryStorage[$chainIdentifier]);
145
+
146
+ return $this->retry($request, $next, $first, $chainIdentifier, $time);
147
+ });
148
+ }
149
+
150
+ /**
151
+ * @param int $retries The number of retries we made before. First time this get called it will be 0.
152
+ */
153
+ public static function defaultErrorResponseDelay(RequestInterface $request, ResponseInterface $response, int $retries): int
154
+ {
155
+ return pow(2, $retries) * 500000;
156
+ }
157
+
158
+ /**
159
+ * @param int $retries The number of retries we made before. First time this get called it will be 0.
160
+ */
161
+ public static function defaultExceptionDelay(RequestInterface $request, ClientExceptionInterface $e, int $retries): int
162
+ {
163
+ return pow(2, $retries) * 500000;
164
+ }
165
+
166
+ /**
167
+ * @throws \Exception if retrying returns a failed promise
168
+ */
169
+ private function retry(RequestInterface $request, callable $next, callable $first, string $chainIdentifier, int $delay): ResponseInterface
170
+ {
171
+ usleep($delay);
172
+
173
+ // Retry synchronously
174
+ ++$this->retryStorage[$chainIdentifier];
175
+ $promise = $this->handleRequest($request, $next, $first);
176
+
177
+ return $promise->wait();
178
+ }
179
+ }
src/vendor/php-http/client-common/src/Plugin/SeekableBodyPlugin.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Client\Common\Plugin;
8
+ use Symfony\Component\OptionsResolver\OptionsResolver;
9
+
10
+ /**
11
+ * @internal
12
+ */
13
+ abstract class SeekableBodyPlugin implements Plugin
14
+ {
15
+ /**
16
+ * @var bool
17
+ */
18
+ protected $useFileBuffer;
19
+
20
+ /**
21
+ * @var int
22
+ */
23
+ protected $memoryBufferSize;
24
+
25
+ /**
26
+ * @param array{'use_file_buffer'?: bool, 'memory_boffer_size'?: int} $config
27
+ *
28
+ * Configuration options:
29
+ * - use_file_buffer: Whether this plugin should use a file as a buffer if the stream is too big, defaults to true
30
+ * - memory_buffer_size: Max memory size in bytes to use for the buffer before it use a file, defaults to 2097152 (2 mb)
31
+ */
32
+ public function __construct(array $config = [])
33
+ {
34
+ $resolver = new OptionsResolver();
35
+ $resolver->setDefaults([
36
+ 'use_file_buffer' => true,
37
+ 'memory_buffer_size' => 2097152,
38
+ ]);
39
+ $resolver->setAllowedTypes('use_file_buffer', 'bool');
40
+ $resolver->setAllowedTypes('memory_buffer_size', 'int');
41
+
42
+ $options = $resolver->resolve($config);
43
+
44
+ $this->useFileBuffer = $options['use_file_buffer'];
45
+ $this->memoryBufferSize = $options['memory_buffer_size'];
46
+ }
47
+ }
src/vendor/php-http/client-common/src/Plugin/VersionBridgePlugin.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common\Plugin;
6
+
7
+ use Http\Promise\Promise;
8
+ use Psr\Http\Message\RequestInterface;
9
+
10
+ /**
11
+ * A plugin that helps you migrate from php-http/client-common 1.x to 2.x. This
12
+ * will also help you to support PHP5 at the same time you support 2.x.
13
+ *
14
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
15
+ */
16
+ trait VersionBridgePlugin
17
+ {
18
+ abstract protected function doHandleRequest(RequestInterface $request, callable $next, callable $first);
19
+
20
+ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
21
+ {
22
+ return $this->doHandleRequest($request, $next, $first);
23
+ }
24
+ }
src/vendor/php-http/client-common/src/PluginChain.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use function array_reverse;
8
+ use Http\Client\Common\Exception\LoopException;
9
+ use Http\Promise\Promise;
10
+ use Psr\Http\Message\RequestInterface;
11
+
12
+ final class PluginChain
13
+ {
14
+ /** @var Plugin[] */
15
+ private $plugins;
16
+
17
+ /** @var callable(RequestInterface): Promise */
18
+ private $clientCallable;
19
+
20
+ /** @var int */
21
+ private $maxRestarts;
22
+
23
+ /** @var int */
24
+ private $restarts = 0;
25
+
26
+ /**
27
+ * @param Plugin[] $plugins A plugin chain
28
+ * @param callable(RequestInterface): Promise $clientCallable Callable making the HTTP call
29
+ * @param array{'max_restarts'?: int} $options
30
+ */
31
+ public function __construct(array $plugins, callable $clientCallable, array $options = [])
32
+ {
33
+ $this->plugins = $plugins;
34
+ $this->clientCallable = $clientCallable;
35
+ $this->maxRestarts = (int) ($options['max_restarts'] ?? 0);
36
+ }
37
+
38
+ private function createChain(): callable
39
+ {
40
+ $lastCallable = $this->clientCallable;
41
+ $reversedPlugins = array_reverse($this->plugins);
42
+
43
+ foreach ($reversedPlugins as $plugin) {
44
+ $lastCallable = function (RequestInterface $request) use ($plugin, $lastCallable) {
45
+ return $plugin->handleRequest($request, $lastCallable, $this);
46
+ };
47
+ }
48
+
49
+ return $lastCallable;
50
+ }
51
+
52
+ public function __invoke(RequestInterface $request): Promise
53
+ {
54
+ if ($this->restarts > $this->maxRestarts) {
55
+ throw new LoopException('Too many restarts in plugin client', $request);
56
+ }
57
+
58
+ ++$this->restarts;
59
+
60
+ return $this->createChain()($request);
61
+ }
62
+ }
src/vendor/php-http/client-common/src/PluginClient.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\Exception as HttplugException;
8
+ use Http\Client\HttpAsyncClient;
9
+ use Http\Client\HttpClient;
10
+ use Http\Client\Promise\HttpFulfilledPromise;
11
+ use Http\Client\Promise\HttpRejectedPromise;
12
+ use Http\Promise\Promise;
13
+ use Psr\Http\Client\ClientInterface;
14
+ use Psr\Http\Message\RequestInterface;
15
+ use Psr\Http\Message\ResponseInterface;
16
+ use Symfony\Component\OptionsResolver\OptionsResolver;
17
+
18
+ /**
19
+ * The client managing plugins and providing a decorator around HTTP Clients.
20
+ *
21
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
22
+ */
23
+ final class PluginClient implements HttpClient, HttpAsyncClient
24
+ {
25
+ /**
26
+ * An HTTP async client.
27
+ *
28
+ * @var HttpAsyncClient
29
+ */
30
+ private $client;
31
+
32
+ /**
33
+ * The plugin chain.
34
+ *
35
+ * @var Plugin[]
36
+ */
37
+ private $plugins;
38
+
39
+ /**
40
+ * A list of options.
41
+ *
42
+ * @var array
43
+ */
44
+ private $options;
45
+
46
+ /**
47
+ * @param ClientInterface|HttpAsyncClient $client An HTTP async client
48
+ * @param Plugin[] $plugins A plugin chain
49
+ * @param array{'max_restarts'?: int} $options
50
+ */
51
+ public function __construct($client, array $plugins = [], array $options = [])
52
+ {
53
+ if ($client instanceof HttpAsyncClient) {
54
+ $this->client = $client;
55
+ } elseif ($client instanceof ClientInterface) {
56
+ $this->client = new EmulatedHttpAsyncClient($client);
57
+ } else {
58
+ throw new \TypeError(
59
+ sprintf('%s::__construct(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
60
+ );
61
+ }
62
+
63
+ $this->plugins = $plugins;
64
+ $this->options = $this->configure($options);
65
+ }
66
+
67
+ /**
68
+ * {@inheritdoc}
69
+ */
70
+ public function sendRequest(RequestInterface $request): ResponseInterface
71
+ {
72
+ // If the client doesn't support sync calls, call async
73
+ if (!$this->client instanceof ClientInterface) {
74
+ return $this->sendAsyncRequest($request)->wait();
75
+ }
76
+
77
+ // Else we want to use the synchronous call of the underlying client,
78
+ // and not the async one in the case we have both an async and sync call
79
+ $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) {
80
+ try {
81
+ return new HttpFulfilledPromise($this->client->sendRequest($request));
82
+ } catch (HttplugException $exception) {
83
+ return new HttpRejectedPromise($exception);
84
+ }
85
+ });
86
+
87
+ return $pluginChain($request)->wait();
88
+ }
89
+
90
+ /**
91
+ * {@inheritdoc}
92
+ */
93
+ public function sendAsyncRequest(RequestInterface $request)
94
+ {
95
+ $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) {
96
+ return $this->client->sendAsyncRequest($request);
97
+ });
98
+
99
+ return $pluginChain($request);
100
+ }
101
+
102
+ /**
103
+ * Configure the plugin client.
104
+ */
105
+ private function configure(array $options = []): array
106
+ {
107
+ $resolver = new OptionsResolver();
108
+ $resolver->setDefaults([
109
+ 'max_restarts' => 10,
110
+ ]);
111
+
112
+ $resolver->setAllowedTypes('max_restarts', 'int');
113
+
114
+ return $resolver->resolve($options);
115
+ }
116
+
117
+ /**
118
+ * Create the plugin chain.
119
+ *
120
+ * @param Plugin[] $plugins A plugin chain
121
+ * @param callable $clientCallable Callable making the HTTP call
122
+ *
123
+ * @return callable(RequestInterface): Promise
124
+ */
125
+ private function createPluginChain(array $plugins, callable $clientCallable): callable
126
+ {
127
+ /** @var callable(RequestInterface): Promise */
128
+ return new PluginChain($plugins, $clientCallable, $this->options);
129
+ }
130
+ }
src/vendor/php-http/client-common/src/PluginClientBuilder.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\HttpAsyncClient;
8
+ use Psr\Http\Client\ClientInterface;
9
+
10
+ /**
11
+ * Build an instance of a PluginClient with a dynamic list of plugins.
12
+ *
13
+ * @author Baptiste Clavié <clavie.b@gmail.com>
14
+ */
15
+ final class PluginClientBuilder
16
+ {
17
+ /** @var Plugin[][] List of plugins ordered by priority [priority => Plugin[]]). */
18
+ private $plugins = [];
19
+
20
+ /** @var array Array of options to give to the plugin client */
21
+ private $options = [];
22
+
23
+ /**
24
+ * @param int $priority Priority of the plugin. The higher comes first.
25
+ */
26
+ public function addPlugin(Plugin $plugin, int $priority = 0): self
27
+ {
28
+ $this->plugins[$priority][] = $plugin;
29
+
30
+ return $this;
31
+ }
32
+
33
+ /**
34
+ * @param mixed $value
35
+ */
36
+ public function setOption(string $name, $value): self
37
+ {
38
+ $this->options[$name] = $value;
39
+
40
+ return $this;
41
+ }
42
+
43
+ public function removeOption(string $name): self
44
+ {
45
+ unset($this->options[$name]);
46
+
47
+ return $this;
48
+ }
49
+
50
+ /**
51
+ * @param ClientInterface|HttpAsyncClient $client
52
+ */
53
+ public function createClient($client): PluginClient
54
+ {
55
+ if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
56
+ throw new \TypeError(
57
+ sprintf('%s::createClient(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
58
+ );
59
+ }
60
+
61
+ $plugins = $this->plugins;
62
+
63
+ if (0 === count($plugins)) {
64
+ $plugins[] = [];
65
+ }
66
+
67
+ krsort($plugins);
68
+ $plugins = array_merge(...$plugins);
69
+
70
+ return new PluginClient(
71
+ $client,
72
+ array_values($plugins),
73
+ $this->options
74
+ );
75
+ }
76
+ }
src/vendor/php-http/client-common/src/PluginClientFactory.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Http\Client\HttpAsyncClient;
8
+ use Psr\Http\Client\ClientInterface;
9
+
10
+ /**
11
+ * Factory to create PluginClient instances. Using this factory instead of calling PluginClient constructor will enable
12
+ * the Symfony profiling without any configuration.
13
+ *
14
+ * @author Fabien Bourigault <bourigaultfabien@gmail.com>
15
+ */
16
+ final class PluginClientFactory
17
+ {
18
+ /**
19
+ * @var (callable(ClientInterface|HttpAsyncClient, Plugin[], array): PluginClient)|null
20
+ */
21
+ private static $factory;
22
+
23
+ /**
24
+ * Set the factory to use.
25
+ * The callable to provide must have the same arguments and return type as PluginClientFactory::createClient.
26
+ * This is used by the HTTPlugBundle to provide a better Symfony integration.
27
+ * Unlike the createClient method, this one is static to allow zero configuration profiling by hooking into early
28
+ * application execution.
29
+ *
30
+ * @internal
31
+ *
32
+ * @param callable(ClientInterface|HttpAsyncClient, Plugin[], array): PluginClient $factory
33
+ */
34
+ public static function setFactory(callable $factory): void
35
+ {
36
+ static::$factory = $factory;
37
+ }
38
+
39
+ /**
40
+ * @param ClientInterface|HttpAsyncClient $client
41
+ * @param Plugin[] $plugins
42
+ * @param array{'client_name'?: string} $options
43
+ *
44
+ * Configuration options:
45
+ * - client_name: to give client a name which may be used when displaying client information
46
+ * like in the HTTPlugBundle profiler.
47
+ *
48
+ * @see PluginClient constructor for PluginClient specific $options.
49
+ */
50
+ public function createClient($client, array $plugins = [], array $options = []): PluginClient
51
+ {
52
+ if (!$client instanceof ClientInterface && !$client instanceof HttpAsyncClient) {
53
+ throw new \TypeError(
54
+ sprintf('%s::createClient(): Argument #1 ($client) must be of type %s|%s, %s given', self::class, ClientInterface::class, HttpAsyncClient::class, get_debug_type($client))
55
+ );
56
+ }
57
+
58
+ if (static::$factory) {
59
+ $factory = static::$factory;
60
+
61
+ return $factory($client, $plugins, $options);
62
+ }
63
+
64
+ unset($options['client_name']);
65
+
66
+ return new PluginClient($client, $plugins, $options);
67
+ }
68
+ }
src/vendor/php-http/client-common/src/VersionBridgeClient.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Common;
6
+
7
+ use Psr\Http\Message\RequestInterface;
8
+ use Psr\Http\Message\ResponseInterface;
9
+
10
+ /**
11
+ * A client that helps you migrate from php-http/httplug 1.x to 2.x. This
12
+ * will also help you to support PHP5 at the same time you support 2.x.
13
+ *
14
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
15
+ */
16
+ trait VersionBridgeClient
17
+ {
18
+ abstract protected function doSendRequest(RequestInterface $request);
19
+
20
+ public function sendRequest(RequestInterface $request): ResponseInterface
21
+ {
22
+ return $this->doSendRequest($request);
23
+ }
24
+ }
src/vendor/php-http/discovery/.php-cs-fixer.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $finder = PhpCsFixer\Finder::create()
4
+ ->in(__DIR__.'/src')
5
+ ->name('*.php')
6
+ ;
7
+
8
+ $config = (new PhpCsFixer\Config())
9
+ ->setRiskyAllowed(true)
10
+ ->setRules([
11
+ '@Symfony' => true,
12
+ ])
13
+ ->setFinder($finder)
14
+ ;
15
+
16
+ return $config;
src/vendor/php-http/discovery/CHANGELOG.md ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Change Log
2
+
3
+ ## 1.14.2 - 2022-05-25
4
+
5
+ - [#202](https://github.com/php-http/discovery/pull/202) - Avoid error when the Symfony PSR-18 client exists but its dependencies are not installed
6
+
7
+ ## 1.14.1 - 2021-09-18
8
+
9
+ - [#199](https://github.com/php-http/discovery/pull/199) - Fixes message factory discovery for `laminas-diactoros ^2.7`
10
+
11
+ ## 1.14.0 - 2021-06-21
12
+
13
+ - Deprecate puli as it has been unmaintained for a long time and is not compatible with composer 2 https://github.com/php-http/discovery/pull/195
14
+
15
+ ## 1.13.0 - 2020-11-27
16
+
17
+ - Support discovering PSR-17 factories of `slim/psr7` package https://github.com/php-http/discovery/pull/192
18
+
19
+ ## 1.12.0 - 2020-09-22
20
+
21
+ - Support discovering HttpClient of `php-http/guzzle7-adapter` https://github.com/php-http/discovery/pull/189
22
+
23
+ ## 1.11.0 - 2020-09-22
24
+
25
+ - Use correct method name to find Uri Factory in PSR17 https://github.com/php-http/discovery/pull/181
26
+
27
+ ## 1.10.0 - 2020-09-04
28
+
29
+ - Discover PSR-18 implementation of phalcon
30
+
31
+ ## 1.9.1 - 2020-07-13
32
+
33
+ ### Fixed
34
+
35
+ - Support PHP 7.4 and 8.0
36
+
37
+ ## 1.9.0 - 2020-07-02
38
+
39
+ ### Added
40
+
41
+ - Support discovering PSR-18 factories of `guzzlehttp/guzzle` 7+
42
+
43
+ ## 1.8.0 - 2020-06-14
44
+
45
+ ### Added
46
+
47
+ - Support discovering PSR-17 factories of `guzzlehttp/psr7` package
48
+ - Support discovering PSR-17 factories of `laminas/laminas-diactoros` package
49
+ - `ClassDiscovery::getStrategies()` to retrieve the list of current strategies.
50
+
51
+ ### Fixed
52
+
53
+ - Ignore exception during discovery when Symfony HttplugClient checks if HTTPlug is available.
54
+
55
+ ## 1.7.4 - 2020-01-03
56
+
57
+ ### Fixed
58
+
59
+ - Improve conditions on Symfony's async HTTPlug client.
60
+
61
+ ## 1.7.3 - 2019-12-27
62
+
63
+ ### Fixed
64
+
65
+ - Enough conditions to only use Symfony HTTP client if all needed components are available.
66
+
67
+ ## 1.7.2 - 2019-12-27
68
+
69
+ ### Fixed
70
+
71
+ - Allow a condition to specify an interface and not just classes.
72
+
73
+ ## 1.7.1 - 2019-12-26
74
+
75
+ ### Fixed
76
+
77
+ - Better conditions to see if Symfony's HTTP clients are available.
78
+
79
+ ## 1.7.0 - 2019-06-30
80
+
81
+ ### Added
82
+
83
+ - Dropped support for PHP < 7.1
84
+ - Support for `symfony/http-client`
85
+
86
+ ## 1.6.1 - 2019-02-23
87
+
88
+ ### Fixed
89
+
90
+ - MockClientStrategy also provides the mock client when requesting an async client
91
+
92
+ ## 1.6.0 - 2019-01-23
93
+
94
+ ### Added
95
+
96
+ - Support for PSR-17 factories
97
+ - Support for PSR-18 clients
98
+
99
+ ## 1.5.2 - 2018-12-31
100
+
101
+ Corrected mistakes in 1.5.1. The different between 1.5.2 and 1.5.0 is that
102
+ we removed some PHP 7 code.
103
+
104
+ https://github.com/php-http/discovery/compare/1.5.0...1.5.2
105
+
106
+ ## 1.5.1 - 2018-12-31
107
+
108
+ This version added new features by mistake. These are reverted in 1.5.2.
109
+
110
+ Do not use 1.5.1.
111
+
112
+ ### Fixed
113
+
114
+ - Removed PHP 7 code
115
+
116
+ ## 1.5.0 - 2018-12-30
117
+
118
+ ### Added
119
+
120
+ - Support for `nyholm/psr7` version 1.0.
121
+ - `ClassDiscovery::safeClassExists` which will help Magento users.
122
+ - Support for HTTPlug 2.0
123
+ - Support for Buzz 1.0
124
+ - Better error message when nothing found by introducing a new exception: `NoCandidateFoundException`.
125
+
126
+ ### Fixed
127
+
128
+ - Fixed condition evaluation, it should stop after first invalid condition.
129
+
130
+ ## 1.4.0 - 2018-02-06
131
+
132
+ ### Added
133
+
134
+ - Discovery support for nyholm/psr7
135
+
136
+ ## 1.3.0 - 2017-08-03
137
+
138
+ ### Added
139
+
140
+ - Discovery support for CakePHP adapter
141
+ - Discovery support for Zend adapter
142
+ - Discovery support for Artax adapter
143
+
144
+ ## 1.2.1 - 2017-03-02
145
+
146
+ ### Fixed
147
+
148
+ - Fixed minor issue with `MockClientStrategy`, also added more tests.
149
+
150
+ ## 1.2.0 - 2017-02-12
151
+
152
+ ### Added
153
+
154
+ - MockClientStrategy class.
155
+
156
+ ## 1.1.1 - 2016-11-27
157
+
158
+ ### Changed
159
+
160
+ - Made exception messages clearer. `StrategyUnavailableException` is no longer the previous exception to `DiscoveryFailedException`.
161
+ - `CommonClassesStrategy` is using `self` instead of `static`. Using `static` makes no sense when `CommonClassesStrategy` is final.
162
+
163
+ ## 1.1.0 - 2016-10-20
164
+
165
+ ### Added
166
+
167
+ - Discovery support for Slim Framework factories
168
+
169
+ ## 1.0.0 - 2016-07-18
170
+
171
+ ### Added
172
+
173
+ - Added back `Http\Discovery\NotFoundException` to preserve BC with 0.8 version. You may upgrade from 0.8.x and 0.9.x to 1.0.0 without any BC breaks.
174
+ - Added interface `Http\Discovery\Exception` which is implemented by all our exceptions
175
+
176
+ ### Changed
177
+
178
+ - Puli strategy renamed to Puli Beta strategy to prevent incompatibility with a future Puli stable
179
+
180
+ ### Deprecated
181
+
182
+ - For BC reasons, the old `Http\Discovery\NotFoundException` (extending the new exception) will be thrown until version 2.0
183
+
184
+
185
+ ## 0.9.1 - 2016-06-28
186
+
187
+ ### Changed
188
+
189
+ - Dropping PHP 5.4 support because we use the ::class constant.
190
+
191
+
192
+ ## 0.9.0 - 2016-06-25
193
+
194
+ ### Added
195
+
196
+ - Discovery strategies to find classes
197
+
198
+ ### Changed
199
+
200
+ - [Puli](http://puli.io) made optional
201
+ - Improved exceptions
202
+ - **[BC] `NotFoundException` moved to `Http\Discovery\Exception\NotFoundException`**
203
+
204
+
205
+ ## 0.8.0 - 2016-02-11
206
+
207
+ ### Changed
208
+
209
+ - Puli composer plugin must be installed separately
210
+
211
+
212
+ ## 0.7.0 - 2016-01-15
213
+
214
+ ### Added
215
+
216
+ - Temporary puli.phar (Beta 10) executable
217
+
218
+ ### Changed
219
+
220
+ - Updated HTTPlug dependencies
221
+ - Updated Puli dependencies
222
+ - Local configuration to make tests passing
223
+
224
+ ### Removed
225
+
226
+ - Puli CLI dependency
227
+
228
+
229
+ ## 0.6.4 - 2016-01-07
230
+
231
+ ### Fixed
232
+
233
+ - Puli [not working](https://twitter.com/PuliPHP/status/685132540588507137) with the latest json-schema
234
+
235
+
236
+ ## 0.6.3 - 2016-01-04
237
+
238
+ ### Changed
239
+
240
+ - Adjust Puli dependencies
241
+
242
+
243
+ ## 0.6.2 - 2016-01-04
244
+
245
+ ### Changed
246
+
247
+ - Make Puli CLI a requirement
248
+
249
+
250
+ ## 0.6.1 - 2016-01-03
251
+
252
+ ### Changed
253
+
254
+ - More flexible Puli requirement
255
+
256
+
257
+ ## 0.6.0 - 2015-12-30
258
+
259
+ ### Changed
260
+
261
+ - Use [Puli](http://puli.io) for discovery
262
+ - Improved exception messages
263
+
264
+
265
+ ## 0.5.0 - 2015-12-25
266
+
267
+ ### Changed
268
+
269
+ - Updated message factory dependency (php-http/message)
270
+
271
+
272
+ ## 0.4.0 - 2015-12-17
273
+
274
+ ### Added
275
+
276
+ - Array condition evaluation in the Class Discovery
277
+
278
+ ### Removed
279
+
280
+ - Message factories (moved to php-http/utils)
281
+
282
+
283
+ ## 0.3.0 - 2015-11-18
284
+
285
+ ### Added
286
+
287
+ - HTTP Async Client Discovery
288
+ - Stream factories
289
+
290
+ ### Changed
291
+
292
+ - Discoveries and Factories are final
293
+ - Message and Uri factories have the type in their names
294
+ - Diactoros Message factory uses Stream factory internally
295
+
296
+ ### Fixed
297
+
298
+ - Improved docblocks for API documentation generation
299
+
300
+
301
+ ## 0.2.0 - 2015-10-31
302
+
303
+ ### Changed
304
+
305
+ - Renamed AdapterDiscovery to ClientDiscovery
306
+
307
+
308
+ ## 0.1.1 - 2015-06-13
309
+
310
+ ### Fixed
311
+
312
+ - Bad HTTP Adapter class name for Guzzle 5
313
+
314
+
315
+ ## 0.1.0 - 2015-06-12
316
+
317
+ ### Added
318
+
319
+ - Initial release
src/vendor/php-http/discovery/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2015-2016 PHP HTTP Team <team@php-http.org>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
src/vendor/php-http/discovery/README.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HTTPlug Discovery
2
+
3
+ [![Latest Version](https://img.shields.io/github/release/php-http/discovery.svg?style=flat-square)](https://github.com/php-http/discovery/releases)
4
+ [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
5
+ [![Build Status](https://img.shields.io/travis/php-http/discovery/master.svg?style=flat-square)](https://travis-ci.org/php-http/discovery)
6
+ [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/discovery.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/discovery)
7
+ [![Quality Score](https://img.shields.io/scrutinizer/g/php-http/discovery.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/discovery)
8
+ [![Total Downloads](https://img.shields.io/packagist/dt/php-http/discovery.svg?style=flat-square)](https://packagist.org/packages/php-http/discovery)
9
+
10
+ **Finds installed HTTPlug implementations and PSR-7 message factories.**
11
+
12
+
13
+ ## Install
14
+
15
+ Via Composer
16
+
17
+ ``` bash
18
+ $ composer require php-http/discovery
19
+ ```
20
+
21
+
22
+ ## Documentation
23
+
24
+ Please see the [official documentation](http://php-http.readthedocs.org/en/latest/discovery.html).
25
+
26
+
27
+ ## Testing
28
+
29
+ ``` bash
30
+ $ composer test
31
+ ```
32
+
33
+
34
+ ## Contributing
35
+
36
+ Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html).
37
+
38
+
39
+ ## Security
40
+
41
+ If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
42
+
43
+
44
+ ## License
45
+
46
+ The MIT License (MIT). Please see [License File](LICENSE) for more information.
src/vendor/php-http/discovery/composer.json ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "php-http/discovery",
3
+ "description": "Finds installed HTTPlug implementations and PSR-7 message factories",
4
+ "license": "MIT",
5
+ "keywords": ["http", "discovery", "client", "adapter", "message", "factory", "psr7"],
6
+ "homepage": "http://php-http.org",
7
+ "authors": [
8
+ {
9
+ "name": "Márk Sági-Kazár",
10
+ "email": "mark.sagikazar@gmail.com"
11
+ }
12
+ ],
13
+ "require": {
14
+ "php": "^7.1 || ^8.0"
15
+ },
16
+ "require-dev": {
17
+ "graham-campbell/phpspec-skip-example-extension": "^5.0",
18
+ "php-http/httplug": "^1.0 || ^2.0",
19
+ "php-http/message-factory": "^1.0",
20
+ "phpspec/phpspec": "^5.1 || ^6.1"
21
+ },
22
+ "suggest": {
23
+ "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories"
24
+ },
25
+ "autoload": {
26
+ "psr-4": {
27
+ "Http\\Discovery\\": "src/"
28
+ }
29
+ },
30
+ "autoload-dev": {
31
+ "psr-4": {
32
+ "spec\\Http\\Discovery\\": "spec/"
33
+ }
34
+ },
35
+ "scripts": {
36
+ "test": "vendor/bin/phpspec run",
37
+ "test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
38
+ },
39
+ "extra": {
40
+ "branch-alias": {
41
+ "dev-master": "1.9-dev"
42
+ }
43
+ },
44
+ "conflict": {
45
+ "nyholm/psr7": "<1.0"
46
+ },
47
+ "prefer-stable": true,
48
+ "minimum-stability": "beta"
49
+ }
src/vendor/php-http/discovery/src/ClassDiscovery.php ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ use Http\Discovery\Exception\ClassInstantiationFailedException;
6
+ use Http\Discovery\Exception\DiscoveryFailedException;
7
+ use Http\Discovery\Exception\NoCandidateFoundException;
8
+ use Http\Discovery\Exception\StrategyUnavailableException;
9
+
10
+ /**
11
+ * Registry that based find results on class existence.
12
+ *
13
+ * @author David de Boer <david@ddeboer.nl>
14
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
15
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
16
+ */
17
+ abstract class ClassDiscovery
18
+ {
19
+ /**
20
+ * A list of strategies to find classes.
21
+ *
22
+ * @var array
23
+ */
24
+ private static $strategies = [
25
+ Strategy\CommonClassesStrategy::class,
26
+ Strategy\CommonPsr17ClassesStrategy::class,
27
+ Strategy\PuliBetaStrategy::class,
28
+ ];
29
+
30
+ private static $deprecatedStrategies = [
31
+ Strategy\PuliBetaStrategy::class => true,
32
+ ];
33
+
34
+ /**
35
+ * Discovery cache to make the second time we use discovery faster.
36
+ *
37
+ * @var array
38
+ */
39
+ private static $cache = [];
40
+
41
+ /**
42
+ * Finds a class.
43
+ *
44
+ * @param string $type
45
+ *
46
+ * @return string|\Closure
47
+ *
48
+ * @throws DiscoveryFailedException
49
+ */
50
+ protected static function findOneByType($type)
51
+ {
52
+ // Look in the cache
53
+ if (null !== ($class = self::getFromCache($type))) {
54
+ return $class;
55
+ }
56
+
57
+ $exceptions = [];
58
+ foreach (self::$strategies as $strategy) {
59
+ try {
60
+ $candidates = call_user_func($strategy.'::getCandidates', $type);
61
+ } catch (StrategyUnavailableException $e) {
62
+ if (!isset(self::$deprecatedStrategies[$strategy])) {
63
+ $exceptions[] = $e;
64
+ }
65
+
66
+ continue;
67
+ }
68
+
69
+ foreach ($candidates as $candidate) {
70
+ if (isset($candidate['condition'])) {
71
+ if (!self::evaluateCondition($candidate['condition'])) {
72
+ continue;
73
+ }
74
+ }
75
+
76
+ // save the result for later use
77
+ self::storeInCache($type, $candidate);
78
+
79
+ return $candidate['class'];
80
+ }
81
+
82
+ $exceptions[] = new NoCandidateFoundException($strategy, $candidates);
83
+ }
84
+
85
+ throw DiscoveryFailedException::create($exceptions);
86
+ }
87
+
88
+ /**
89
+ * Get a value from cache.
90
+ *
91
+ * @param string $type
92
+ *
93
+ * @return string|null
94
+ */
95
+ private static function getFromCache($type)
96
+ {
97
+ if (!isset(self::$cache[$type])) {
98
+ return;
99
+ }
100
+
101
+ $candidate = self::$cache[$type];
102
+ if (isset($candidate['condition'])) {
103
+ if (!self::evaluateCondition($candidate['condition'])) {
104
+ return;
105
+ }
106
+ }
107
+
108
+ return $candidate['class'];
109
+ }
110
+
111
+ /**
112
+ * Store a value in cache.
113
+ *
114
+ * @param string $type
115
+ * @param string $class
116
+ */
117
+ private static function storeInCache($type, $class)
118
+ {
119
+ self::$cache[$type] = $class;
120
+ }
121
+
122
+ /**
123
+ * Set new strategies and clear the cache.
124
+ *
125
+ * @param array $strategies string array of fully qualified class name to a DiscoveryStrategy
126
+ */
127
+ public static function setStrategies(array $strategies)
128
+ {
129
+ self::$strategies = $strategies;
130
+ self::clearCache();
131
+ }
132
+
133
+ /**
134
+ * Returns the currently configured discovery strategies as fully qualified class names.
135
+ *
136
+ * @return string[]
137
+ */
138
+ public static function getStrategies(): iterable
139
+ {
140
+ return self::$strategies;
141
+ }
142
+
143
+ /**
144
+ * Append a strategy at the end of the strategy queue.
145
+ *
146
+ * @param string $strategy Fully qualified class name to a DiscoveryStrategy
147
+ */
148
+ public static function appendStrategy($strategy)
149
+ {
150
+ self::$strategies[] = $strategy;
151
+ self::clearCache();
152
+ }
153
+
154
+ /**
155
+ * Prepend a strategy at the beginning of the strategy queue.
156
+ *
157
+ * @param string $strategy Fully qualified class name to a DiscoveryStrategy
158
+ */
159
+ public static function prependStrategy($strategy)
160
+ {
161
+ array_unshift(self::$strategies, $strategy);
162
+ self::clearCache();
163
+ }
164
+
165
+ /**
166
+ * Clear the cache.
167
+ */
168
+ public static function clearCache()
169
+ {
170
+ self::$cache = [];
171
+ }
172
+
173
+ /**
174
+ * Evaluates conditions to boolean.
175
+ *
176
+ * @param mixed $condition
177
+ *
178
+ * @return bool
179
+ */
180
+ protected static function evaluateCondition($condition)
181
+ {
182
+ if (is_string($condition)) {
183
+ // Should be extended for functions, extensions???
184
+ return self::safeClassExists($condition);
185
+ }
186
+ if (is_callable($condition)) {
187
+ return (bool) $condition();
188
+ }
189
+ if (is_bool($condition)) {
190
+ return $condition;
191
+ }
192
+ if (is_array($condition)) {
193
+ foreach ($condition as $c) {
194
+ if (false === static::evaluateCondition($c)) {
195
+ // Immediately stop execution if the condition is false
196
+ return false;
197
+ }
198
+ }
199
+
200
+ return true;
201
+ }
202
+
203
+ return false;
204
+ }
205
+
206
+ /**
207
+ * Get an instance of the $class.
208
+ *
209
+ * @param string|\Closure $class a FQCN of a class or a closure that instantiate the class
210
+ *
211
+ * @return object
212
+ *
213
+ * @throws ClassInstantiationFailedException
214
+ */
215
+ protected static function instantiateClass($class)
216
+ {
217
+ try {
218
+ if (is_string($class)) {
219
+ return new $class();
220
+ }
221
+
222
+ if (is_callable($class)) {
223
+ return $class();
224
+ }
225
+ } catch (\Exception $e) {
226
+ throw new ClassInstantiationFailedException('Unexpected exception when instantiating class.', 0, $e);
227
+ }
228
+
229
+ throw new ClassInstantiationFailedException('Could not instantiate class because parameter is neither a callable nor a string');
230
+ }
231
+
232
+ /**
233
+ * We want to do a "safe" version of PHP's "class_exists" because Magento has a bug
234
+ * (or they call it a "feature"). Magento is throwing an exception if you do class_exists()
235
+ * on a class that ends with "Factory" and if that file does not exits.
236
+ *
237
+ * This function will catch all potential exceptions and make sure it returns a boolean.
238
+ *
239
+ * @param string $class
240
+ * @param bool $autoload
241
+ *
242
+ * @return bool
243
+ */
244
+ public static function safeClassExists($class)
245
+ {
246
+ try {
247
+ return class_exists($class) || interface_exists($class);
248
+ } catch (\Exception $e) {
249
+ return false;
250
+ }
251
+ }
252
+ }
src/vendor/php-http/discovery/src/Exception.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ /**
6
+ * An interface implemented by all discovery related exceptions.
7
+ *
8
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
9
+ */
10
+ interface Exception
11
+ {
12
+ }
src/vendor/php-http/discovery/src/Exception/ClassInstantiationFailedException.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Exception;
4
+
5
+ use Http\Discovery\Exception;
6
+
7
+ /**
8
+ * Thrown when a class fails to instantiate.
9
+ *
10
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
11
+ */
12
+ final class ClassInstantiationFailedException extends \RuntimeException implements Exception
13
+ {
14
+ }
src/vendor/php-http/discovery/src/Exception/DiscoveryFailedException.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Exception;
4
+
5
+ use Http\Discovery\Exception;
6
+
7
+ /**
8
+ * Thrown when all discovery strategies fails to find a resource.
9
+ *
10
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
11
+ */
12
+ final class DiscoveryFailedException extends \Exception implements Exception
13
+ {
14
+ /**
15
+ * @var \Exception[]
16
+ */
17
+ private $exceptions;
18
+
19
+ /**
20
+ * @param string $message
21
+ * @param \Exception[] $exceptions
22
+ */
23
+ public function __construct($message, array $exceptions = [])
24
+ {
25
+ $this->exceptions = $exceptions;
26
+
27
+ parent::__construct($message);
28
+ }
29
+
30
+ /**
31
+ * @param \Exception[] $exceptions
32
+ */
33
+ public static function create($exceptions)
34
+ {
35
+ $message = 'Could not find resource using any discovery strategy. Find more information at http://docs.php-http.org/en/latest/discovery.html#common-errors';
36
+ foreach ($exceptions as $e) {
37
+ $message .= "\n - ".$e->getMessage();
38
+ }
39
+ $message .= "\n\n";
40
+
41
+ return new self($message, $exceptions);
42
+ }
43
+
44
+ /**
45
+ * @return \Exception[]
46
+ */
47
+ public function getExceptions()
48
+ {
49
+ return $this->exceptions;
50
+ }
51
+ }
src/vendor/php-http/discovery/src/Exception/NoCandidateFoundException.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Exception;
4
+
5
+ use Http\Discovery\Exception;
6
+
7
+ /**
8
+ * When we have used a strategy but no candidates provided by that strategy could be used.
9
+ *
10
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
11
+ */
12
+ final class NoCandidateFoundException extends \Exception implements Exception
13
+ {
14
+ /**
15
+ * @param string $strategy
16
+ */
17
+ public function __construct($strategy, array $candidates)
18
+ {
19
+ $classes = array_map(
20
+ function ($a) {
21
+ return $a['class'];
22
+ },
23
+ $candidates
24
+ );
25
+
26
+ $message = sprintf(
27
+ 'No valid candidate found using strategy "%s". We tested the following candidates: %s.',
28
+ $strategy,
29
+ implode(', ', array_map([$this, 'stringify'], $classes))
30
+ );
31
+
32
+ parent::__construct($message);
33
+ }
34
+
35
+ private function stringify($mixed)
36
+ {
37
+ if (is_string($mixed)) {
38
+ return $mixed;
39
+ }
40
+
41
+ if (is_array($mixed) && 2 === count($mixed)) {
42
+ return sprintf('%s::%s', $this->stringify($mixed[0]), $mixed[1]);
43
+ }
44
+
45
+ return is_object($mixed) ? get_class($mixed) : gettype($mixed);
46
+ }
47
+ }
src/vendor/php-http/discovery/src/Exception/NotFoundException.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Exception;
4
+
5
+ use Http\Discovery\Exception;
6
+
7
+ /**
8
+ * Thrown when a discovery does not find any matches.
9
+ *
10
+ * @final do NOT extend this class, not final for BC reasons
11
+ *
12
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
13
+ */
14
+ /* final */ class NotFoundException extends \RuntimeException implements Exception
15
+ {
16
+ }
src/vendor/php-http/discovery/src/Exception/PuliUnavailableException.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Exception;
4
+
5
+ /**
6
+ * Thrown when we can't use Puli for discovery.
7
+ *
8
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
9
+ */
10
+ final class PuliUnavailableException extends StrategyUnavailableException
11
+ {
12
+ }
src/vendor/php-http/discovery/src/Exception/StrategyUnavailableException.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Exception;
4
+
5
+ use Http\Discovery\Exception;
6
+
7
+ /**
8
+ * This exception is thrown when we cannot use a discovery strategy. This is *not* thrown when
9
+ * the discovery fails to find a class.
10
+ *
11
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
12
+ */
13
+ class StrategyUnavailableException extends \RuntimeException implements Exception
14
+ {
15
+ }
src/vendor/php-http/discovery/src/HttpAsyncClientDiscovery.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ use Http\Client\HttpAsyncClient;
6
+ use Http\Discovery\Exception\DiscoveryFailedException;
7
+
8
+ /**
9
+ * Finds an HTTP Asynchronous Client.
10
+ *
11
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
12
+ */
13
+ final class HttpAsyncClientDiscovery extends ClassDiscovery
14
+ {
15
+ /**
16
+ * Finds an HTTP Async Client.
17
+ *
18
+ * @return HttpAsyncClient
19
+ *
20
+ * @throws Exception\NotFoundException
21
+ */
22
+ public static function find()
23
+ {
24
+ try {
25
+ $asyncClient = static::findOneByType(HttpAsyncClient::class);
26
+ } catch (DiscoveryFailedException $e) {
27
+ throw new NotFoundException('No HTTPlug async clients found. Make sure to install a package providing "php-http/async-client-implementation". Example: "php-http/guzzle6-adapter".', 0, $e);
28
+ }
29
+
30
+ return static::instantiateClass($asyncClient);
31
+ }
32
+ }
src/vendor/php-http/discovery/src/HttpClientDiscovery.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ use Http\Client\HttpClient;
6
+ use Http\Discovery\Exception\DiscoveryFailedException;
7
+
8
+ /**
9
+ * Finds an HTTP Client.
10
+ *
11
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
12
+ */
13
+ final class HttpClientDiscovery extends ClassDiscovery
14
+ {
15
+ /**
16
+ * Finds an HTTP Client.
17
+ *
18
+ * @return HttpClient
19
+ *
20
+ * @throws Exception\NotFoundException
21
+ */
22
+ public static function find()
23
+ {
24
+ try {
25
+ $client = static::findOneByType(HttpClient::class);
26
+ } catch (DiscoveryFailedException $e) {
27
+ throw new NotFoundException('No HTTPlug clients found. Make sure to install a package providing "php-http/client-implementation". Example: "php-http/guzzle6-adapter".', 0, $e);
28
+ }
29
+
30
+ return static::instantiateClass($client);
31
+ }
32
+ }
src/vendor/php-http/discovery/src/MessageFactoryDiscovery.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ use Http\Discovery\Exception\DiscoveryFailedException;
6
+ use Http\Message\MessageFactory;
7
+
8
+ /**
9
+ * Finds a Message Factory.
10
+ *
11
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
12
+ *
13
+ * @deprecated This will be removed in 2.0. Consider using Psr17FactoryDiscovery.
14
+ */
15
+ final class MessageFactoryDiscovery extends ClassDiscovery
16
+ {
17
+ /**
18
+ * Finds a Message Factory.
19
+ *
20
+ * @return MessageFactory
21
+ *
22
+ * @throws Exception\NotFoundException
23
+ */
24
+ public static function find()
25
+ {
26
+ try {
27
+ $messageFactory = static::findOneByType(MessageFactory::class);
28
+ } catch (DiscoveryFailedException $e) {
29
+ throw new NotFoundException('No message factories found. To use Guzzle, Diactoros or Slim Framework factories install php-http/message and the chosen message implementation.', 0, $e);
30
+ }
31
+
32
+ return static::instantiateClass($messageFactory);
33
+ }
34
+ }
src/vendor/php-http/discovery/src/NotFoundException.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ /**
6
+ * Thrown when a discovery does not find any matches.
7
+ *
8
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
9
+ *
10
+ * @deprecated since since version 1.0, and will be removed in 2.0. Use {@link \Http\Discovery\Exception\NotFoundException} instead.
11
+ */
12
+ final class NotFoundException extends \Http\Discovery\Exception\NotFoundException
13
+ {
14
+ }
src/vendor/php-http/discovery/src/Psr17FactoryDiscovery.php ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ use Http\Discovery\Exception\DiscoveryFailedException;
6
+ use Psr\Http\Message\RequestFactoryInterface;
7
+ use Psr\Http\Message\ResponseFactoryInterface;
8
+ use Psr\Http\Message\ServerRequestFactoryInterface;
9
+ use Psr\Http\Message\StreamFactoryInterface;
10
+ use Psr\Http\Message\UploadedFileFactoryInterface;
11
+ use Psr\Http\Message\UriFactoryInterface;
12
+
13
+ /**
14
+ * Finds PSR-17 factories.
15
+ *
16
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
17
+ */
18
+ final class Psr17FactoryDiscovery extends ClassDiscovery
19
+ {
20
+ private static function createException($type, Exception $e)
21
+ {
22
+ return new \Http\Discovery\Exception\NotFoundException(
23
+ 'No PSR-17 '.$type.' found. Install a package from this list: https://packagist.org/providers/psr/http-factory-implementation',
24
+ 0,
25
+ $e
26
+ );
27
+ }
28
+
29
+ /**
30
+ * @return RequestFactoryInterface
31
+ *
32
+ * @throws Exception\NotFoundException
33
+ */
34
+ public static function findRequestFactory()
35
+ {
36
+ try {
37
+ $messageFactory = static::findOneByType(RequestFactoryInterface::class);
38
+ } catch (DiscoveryFailedException $e) {
39
+ throw self::createException('request factory', $e);
40
+ }
41
+
42
+ return static::instantiateClass($messageFactory);
43
+ }
44
+
45
+ /**
46
+ * @return ResponseFactoryInterface
47
+ *
48
+ * @throws Exception\NotFoundException
49
+ */
50
+ public static function findResponseFactory()
51
+ {
52
+ try {
53
+ $messageFactory = static::findOneByType(ResponseFactoryInterface::class);
54
+ } catch (DiscoveryFailedException $e) {
55
+ throw self::createException('response factory', $e);
56
+ }
57
+
58
+ return static::instantiateClass($messageFactory);
59
+ }
60
+
61
+ /**
62
+ * @return ServerRequestFactoryInterface
63
+ *
64
+ * @throws Exception\NotFoundException
65
+ */
66
+ public static function findServerRequestFactory()
67
+ {
68
+ try {
69
+ $messageFactory = static::findOneByType(ServerRequestFactoryInterface::class);
70
+ } catch (DiscoveryFailedException $e) {
71
+ throw self::createException('server request factory', $e);
72
+ }
73
+
74
+ return static::instantiateClass($messageFactory);
75
+ }
76
+
77
+ /**
78
+ * @return StreamFactoryInterface
79
+ *
80
+ * @throws Exception\NotFoundException
81
+ */
82
+ public static function findStreamFactory()
83
+ {
84
+ try {
85
+ $messageFactory = static::findOneByType(StreamFactoryInterface::class);
86
+ } catch (DiscoveryFailedException $e) {
87
+ throw self::createException('stream factory', $e);
88
+ }
89
+
90
+ return static::instantiateClass($messageFactory);
91
+ }
92
+
93
+ /**
94
+ * @return UploadedFileFactoryInterface
95
+ *
96
+ * @throws Exception\NotFoundException
97
+ */
98
+ public static function findUploadedFileFactory()
99
+ {
100
+ try {
101
+ $messageFactory = static::findOneByType(UploadedFileFactoryInterface::class);
102
+ } catch (DiscoveryFailedException $e) {
103
+ throw self::createException('uploaded file factory', $e);
104
+ }
105
+
106
+ return static::instantiateClass($messageFactory);
107
+ }
108
+
109
+ /**
110
+ * @return UriFactoryInterface
111
+ *
112
+ * @throws Exception\NotFoundException
113
+ */
114
+ public static function findUriFactory()
115
+ {
116
+ try {
117
+ $messageFactory = static::findOneByType(UriFactoryInterface::class);
118
+ } catch (DiscoveryFailedException $e) {
119
+ throw self::createException('url factory', $e);
120
+ }
121
+
122
+ return static::instantiateClass($messageFactory);
123
+ }
124
+
125
+ /**
126
+ * @return UriFactoryInterface
127
+ *
128
+ * @throws Exception\NotFoundException
129
+ *
130
+ * @deprecated This will be removed in 2.0. Consider using the findUriFactory() method.
131
+ */
132
+ public static function findUrlFactory()
133
+ {
134
+ return static::findUriFactory();
135
+ }
136
+ }
src/vendor/php-http/discovery/src/Psr18ClientDiscovery.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ use Http\Discovery\Exception\DiscoveryFailedException;
6
+ use Psr\Http\Client\ClientInterface;
7
+
8
+ /**
9
+ * Finds a PSR-18 HTTP Client.
10
+ *
11
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
12
+ */
13
+ final class Psr18ClientDiscovery extends ClassDiscovery
14
+ {
15
+ /**
16
+ * Finds a PSR-18 HTTP Client.
17
+ *
18
+ * @return ClientInterface
19
+ *
20
+ * @throws Exception\NotFoundException
21
+ */
22
+ public static function find()
23
+ {
24
+ try {
25
+ $client = static::findOneByType(ClientInterface::class);
26
+ } catch (DiscoveryFailedException $e) {
27
+ throw new \Http\Discovery\Exception\NotFoundException('No PSR-18 clients found. Make sure to install a package providing "psr/http-client-implementation". Example: "php-http/guzzle7-adapter".', 0, $e);
28
+ }
29
+
30
+ return static::instantiateClass($client);
31
+ }
32
+ }
src/vendor/php-http/discovery/src/Strategy/CommonClassesStrategy.php ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Strategy;
4
+
5
+ use GuzzleHttp\Client as GuzzleHttp;
6
+ use GuzzleHttp\Promise\Promise;
7
+ use GuzzleHttp\Psr7\Request as GuzzleRequest;
8
+ use Http\Adapter\Artax\Client as Artax;
9
+ use Http\Adapter\Buzz\Client as Buzz;
10
+ use Http\Adapter\Cake\Client as Cake;
11
+ use Http\Adapter\Guzzle5\Client as Guzzle5;
12
+ use Http\Adapter\Guzzle6\Client as Guzzle6;
13
+ use Http\Adapter\Guzzle7\Client as Guzzle7;
14
+ use Http\Adapter\React\Client as React;
15
+ use Http\Adapter\Zend\Client as Zend;
16
+ use Http\Client\Curl\Client as Curl;
17
+ use Http\Client\HttpAsyncClient;
18
+ use Http\Client\HttpClient;
19
+ use Http\Client\Socket\Client as Socket;
20
+ use Http\Discovery\ClassDiscovery;
21
+ use Http\Discovery\Exception\NotFoundException;
22
+ use Http\Discovery\MessageFactoryDiscovery;
23
+ use Http\Discovery\Psr17FactoryDiscovery;
24
+ use Http\Message\MessageFactory;
25
+ use Http\Message\MessageFactory\DiactorosMessageFactory;
26
+ use Http\Message\MessageFactory\GuzzleMessageFactory;
27
+ use Http\Message\MessageFactory\SlimMessageFactory;
28
+ use Http\Message\RequestFactory;
29
+ use Http\Message\StreamFactory;
30
+ use Http\Message\StreamFactory\DiactorosStreamFactory;
31
+ use Http\Message\StreamFactory\GuzzleStreamFactory;
32
+ use Http\Message\StreamFactory\SlimStreamFactory;
33
+ use Http\Message\UriFactory;
34
+ use Http\Message\UriFactory\DiactorosUriFactory;
35
+ use Http\Message\UriFactory\GuzzleUriFactory;
36
+ use Http\Message\UriFactory\SlimUriFactory;
37
+ use Laminas\Diactoros\Request as DiactorosRequest;
38
+ use Nyholm\Psr7\Factory\HttplugFactory as NyholmHttplugFactory;
39
+ use Psr\Http\Client\ClientInterface as Psr18Client;
40
+ use Psr\Http\Message\RequestFactoryInterface as Psr17RequestFactory;
41
+ use Slim\Http\Request as SlimRequest;
42
+ use Symfony\Component\HttpClient\HttplugClient as SymfonyHttplug;
43
+ use Symfony\Component\HttpClient\Psr18Client as SymfonyPsr18;
44
+ use Zend\Diactoros\Request as ZendDiactorosRequest;
45
+
46
+ /**
47
+ * @internal
48
+ *
49
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
50
+ */
51
+ final class CommonClassesStrategy implements DiscoveryStrategy
52
+ {
53
+ /**
54
+ * @var array
55
+ */
56
+ private static $classes = [
57
+ MessageFactory::class => [
58
+ ['class' => NyholmHttplugFactory::class, 'condition' => [NyholmHttplugFactory::class]],
59
+ ['class' => GuzzleMessageFactory::class, 'condition' => [GuzzleRequest::class, GuzzleMessageFactory::class]],
60
+ ['class' => DiactorosMessageFactory::class, 'condition' => [ZendDiactorosRequest::class, DiactorosMessageFactory::class]],
61
+ ['class' => DiactorosMessageFactory::class, 'condition' => [DiactorosRequest::class, DiactorosMessageFactory::class]],
62
+ ['class' => SlimMessageFactory::class, 'condition' => [SlimRequest::class, SlimMessageFactory::class]],
63
+ ],
64
+ StreamFactory::class => [
65
+ ['class' => NyholmHttplugFactory::class, 'condition' => [NyholmHttplugFactory::class]],
66
+ ['class' => GuzzleStreamFactory::class, 'condition' => [GuzzleRequest::class, GuzzleStreamFactory::class]],
67
+ ['class' => DiactorosStreamFactory::class, 'condition' => [ZendDiactorosRequest::class, DiactorosStreamFactory::class]],
68
+ ['class' => DiactorosStreamFactory::class, 'condition' => [DiactorosRequest::class, DiactorosStreamFactory::class]],
69
+ ['class' => SlimStreamFactory::class, 'condition' => [SlimRequest::class, SlimStreamFactory::class]],
70
+ ],
71
+ UriFactory::class => [
72
+ ['class' => NyholmHttplugFactory::class, 'condition' => [NyholmHttplugFactory::class]],
73
+ ['class' => GuzzleUriFactory::class, 'condition' => [GuzzleRequest::class, GuzzleUriFactory::class]],
74
+ ['class' => DiactorosUriFactory::class, 'condition' => [ZendDiactorosRequest::class, DiactorosUriFactory::class]],
75
+ ['class' => DiactorosUriFactory::class, 'condition' => [DiactorosRequest::class, DiactorosUriFactory::class]],
76
+ ['class' => SlimUriFactory::class, 'condition' => [SlimRequest::class, SlimUriFactory::class]],
77
+ ],
78
+ HttpAsyncClient::class => [
79
+ ['class' => SymfonyHttplug::class, 'condition' => [SymfonyHttplug::class, Promise::class, RequestFactory::class, [self::class, 'isPsr17FactoryInstalled']]],
80
+ ['class' => Guzzle7::class, 'condition' => Guzzle7::class],
81
+ ['class' => Guzzle6::class, 'condition' => Guzzle6::class],
82
+ ['class' => Curl::class, 'condition' => Curl::class],
83
+ ['class' => React::class, 'condition' => React::class],
84
+ ],
85
+ HttpClient::class => [
86
+ ['class' => SymfonyHttplug::class, 'condition' => [SymfonyHttplug::class, RequestFactory::class, [self::class, 'isPsr17FactoryInstalled']]],
87
+ ['class' => Guzzle7::class, 'condition' => Guzzle7::class],
88
+ ['class' => Guzzle6::class, 'condition' => Guzzle6::class],
89
+ ['class' => Guzzle5::class, 'condition' => Guzzle5::class],
90
+ ['class' => Curl::class, 'condition' => Curl::class],
91
+ ['class' => Socket::class, 'condition' => Socket::class],
92
+ ['class' => Buzz::class, 'condition' => Buzz::class],
93
+ ['class' => React::class, 'condition' => React::class],
94
+ ['class' => Cake::class, 'condition' => Cake::class],
95
+ ['class' => Zend::class, 'condition' => Zend::class],
96
+ ['class' => Artax::class, 'condition' => Artax::class],
97
+ [
98
+ 'class' => [self::class, 'buzzInstantiate'],
99
+ 'condition' => [\Buzz\Client\FileGetContents::class, \Buzz\Message\ResponseBuilder::class],
100
+ ],
101
+ ],
102
+ Psr18Client::class => [
103
+ [
104
+ 'class' => [self::class, 'symfonyPsr18Instantiate'],
105
+ 'condition' => [SymfonyPsr18::class, Psr17RequestFactory::class],
106
+ ],
107
+ [
108
+ 'class' => GuzzleHttp::class,
109
+ 'condition' => [self::class, 'isGuzzleImplementingPsr18'],
110
+ ],
111
+ [
112
+ 'class' => [self::class, 'buzzInstantiate'],
113
+ 'condition' => [\Buzz\Client\FileGetContents::class, \Buzz\Message\ResponseBuilder::class],
114
+ ],
115
+ ],
116
+ ];
117
+
118
+ /**
119
+ * {@inheritdoc}
120
+ */
121
+ public static function getCandidates($type)
122
+ {
123
+ if (Psr18Client::class === $type) {
124
+ return self::getPsr18Candidates();
125
+ }
126
+
127
+ return self::$classes[$type] ?? [];
128
+ }
129
+
130
+ /**
131
+ * @return array The return value is always an array with zero or more elements. Each
132
+ * element is an array with two keys ['class' => string, 'condition' => mixed].
133
+ */
134
+ private static function getPsr18Candidates()
135
+ {
136
+ $candidates = self::$classes[Psr18Client::class];
137
+
138
+ // HTTPlug 2.0 clients implements PSR18Client too.
139
+ foreach (self::$classes[HttpClient::class] as $c) {
140
+ if (!is_string($c['class'])) {
141
+ continue;
142
+ }
143
+ try {
144
+ if (ClassDiscovery::safeClassExists($c['class']) && is_subclass_of($c['class'], Psr18Client::class)) {
145
+ $candidates[] = $c;
146
+ }
147
+ } catch (\Throwable $e) {
148
+ trigger_error(sprintf('Got exception "%s (%s)" while checking if a PSR-18 Client is available', get_class($e), $e->getMessage()), E_USER_WARNING);
149
+ }
150
+ }
151
+
152
+ return $candidates;
153
+ }
154
+
155
+ public static function buzzInstantiate()
156
+ {
157
+ return new \Buzz\Client\FileGetContents(MessageFactoryDiscovery::find());
158
+ }
159
+
160
+ public static function symfonyPsr18Instantiate()
161
+ {
162
+ return new SymfonyPsr18(null, Psr17FactoryDiscovery::findResponseFactory(), Psr17FactoryDiscovery::findStreamFactory());
163
+ }
164
+
165
+ public static function isGuzzleImplementingPsr18()
166
+ {
167
+ return defined('GuzzleHttp\ClientInterface::MAJOR_VERSION');
168
+ }
169
+
170
+ /**
171
+ * Can be used as a condition.
172
+ *
173
+ * @return bool
174
+ */
175
+ public static function isPsr17FactoryInstalled()
176
+ {
177
+ try {
178
+ Psr17FactoryDiscovery::findResponseFactory();
179
+ } catch (NotFoundException $e) {
180
+ return false;
181
+ } catch (\Throwable $e) {
182
+ trigger_error(sprintf('Got exception "%s (%s)" while checking if a PSR-17 ResponseFactory is available', get_class($e), $e->getMessage()), E_USER_WARNING);
183
+
184
+ return false;
185
+ }
186
+
187
+ return true;
188
+ }
189
+ }
src/vendor/php-http/discovery/src/Strategy/CommonPsr17ClassesStrategy.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Strategy;
4
+
5
+ use Psr\Http\Message\RequestFactoryInterface;
6
+ use Psr\Http\Message\ResponseFactoryInterface;
7
+ use Psr\Http\Message\ServerRequestFactoryInterface;
8
+ use Psr\Http\Message\StreamFactoryInterface;
9
+ use Psr\Http\Message\UploadedFileFactoryInterface;
10
+ use Psr\Http\Message\UriFactoryInterface;
11
+
12
+ /**
13
+ * @internal
14
+ *
15
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
16
+ */
17
+ final class CommonPsr17ClassesStrategy implements DiscoveryStrategy
18
+ {
19
+ /**
20
+ * @var array
21
+ */
22
+ private static $classes = [
23
+ RequestFactoryInterface::class => [
24
+ 'Phalcon\Http\Message\RequestFactory',
25
+ 'Nyholm\Psr7\Factory\Psr17Factory',
26
+ 'Zend\Diactoros\RequestFactory',
27
+ 'GuzzleHttp\Psr7\HttpFactory',
28
+ 'Http\Factory\Diactoros\RequestFactory',
29
+ 'Http\Factory\Guzzle\RequestFactory',
30
+ 'Http\Factory\Slim\RequestFactory',
31
+ 'Laminas\Diactoros\RequestFactory',
32
+ 'Slim\Psr7\Factory\RequestFactory',
33
+ ],
34
+ ResponseFactoryInterface::class => [
35
+ 'Phalcon\Http\Message\ResponseFactory',
36
+ 'Nyholm\Psr7\Factory\Psr17Factory',
37
+ 'Zend\Diactoros\ResponseFactory',
38
+ 'GuzzleHttp\Psr7\HttpFactory',
39
+ 'Http\Factory\Diactoros\ResponseFactory',
40
+ 'Http\Factory\Guzzle\ResponseFactory',
41
+ 'Http\Factory\Slim\ResponseFactory',
42
+ 'Laminas\Diactoros\ResponseFactory',
43
+ 'Slim\Psr7\Factory\ResponseFactory',
44
+ ],
45
+ ServerRequestFactoryInterface::class => [
46
+ 'Phalcon\Http\Message\ServerRequestFactory',
47
+ 'Nyholm\Psr7\Factory\Psr17Factory',
48
+ 'Zend\Diactoros\ServerRequestFactory',
49
+ 'GuzzleHttp\Psr7\HttpFactory',
50
+ 'Http\Factory\Diactoros\ServerRequestFactory',
51
+ 'Http\Factory\Guzzle\ServerRequestFactory',
52
+ 'Http\Factory\Slim\ServerRequestFactory',
53
+ 'Laminas\Diactoros\ServerRequestFactory',
54
+ 'Slim\Psr7\Factory\ServerRequestFactory',
55
+ ],
56
+ StreamFactoryInterface::class => [
57
+ 'Phalcon\Http\Message\StreamFactory',
58
+ 'Nyholm\Psr7\Factory\Psr17Factory',
59
+ 'Zend\Diactoros\StreamFactory',
60
+ 'GuzzleHttp\Psr7\HttpFactory',
61
+ 'Http\Factory\Diactoros\StreamFactory',
62
+ 'Http\Factory\Guzzle\StreamFactory',
63
+ 'Http\Factory\Slim\StreamFactory',
64
+ 'Laminas\Diactoros\StreamFactory',
65
+ 'Slim\Psr7\Factory\StreamFactory',
66
+ ],
67
+ UploadedFileFactoryInterface::class => [
68
+ 'Phalcon\Http\Message\UploadedFileFactory',
69
+ 'Nyholm\Psr7\Factory\Psr17Factory',
70
+ 'Zend\Diactoros\UploadedFileFactory',
71
+ 'GuzzleHttp\Psr7\HttpFactory',
72
+ 'Http\Factory\Diactoros\UploadedFileFactory',
73
+ 'Http\Factory\Guzzle\UploadedFileFactory',
74
+ 'Http\Factory\Slim\UploadedFileFactory',
75
+ 'Laminas\Diactoros\UploadedFileFactory',
76
+ 'Slim\Psr7\Factory\UploadedFileFactory',
77
+ ],
78
+ UriFactoryInterface::class => [
79
+ 'Phalcon\Http\Message\UriFactory',
80
+ 'Nyholm\Psr7\Factory\Psr17Factory',
81
+ 'Zend\Diactoros\UriFactory',
82
+ 'GuzzleHttp\Psr7\HttpFactory',
83
+ 'Http\Factory\Diactoros\UriFactory',
84
+ 'Http\Factory\Guzzle\UriFactory',
85
+ 'Http\Factory\Slim\UriFactory',
86
+ 'Laminas\Diactoros\UriFactory',
87
+ 'Slim\Psr7\Factory\UriFactory',
88
+ ],
89
+ ];
90
+
91
+ /**
92
+ * {@inheritdoc}
93
+ */
94
+ public static function getCandidates($type)
95
+ {
96
+ $candidates = [];
97
+ if (isset(self::$classes[$type])) {
98
+ foreach (self::$classes[$type] as $class) {
99
+ $candidates[] = ['class' => $class, 'condition' => [$class]];
100
+ }
101
+ }
102
+
103
+ return $candidates;
104
+ }
105
+ }
src/vendor/php-http/discovery/src/Strategy/DiscoveryStrategy.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Strategy;
4
+
5
+ use Http\Discovery\Exception\StrategyUnavailableException;
6
+
7
+ /**
8
+ * @author Tobias Nyholm <tobias.nyholm@gmail.com>
9
+ */
10
+ interface DiscoveryStrategy
11
+ {
12
+ /**
13
+ * Find a resource of a specific type.
14
+ *
15
+ * @param string $type
16
+ *
17
+ * @return array The return value is always an array with zero or more elements. Each
18
+ * element is an array with two keys ['class' => string, 'condition' => mixed].
19
+ *
20
+ * @throws StrategyUnavailableException if we cannot use this strategy
21
+ */
22
+ public static function getCandidates($type);
23
+ }
src/vendor/php-http/discovery/src/Strategy/MockClientStrategy.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Strategy;
4
+
5
+ use Http\Client\HttpAsyncClient;
6
+ use Http\Client\HttpClient;
7
+ use Http\Mock\Client as Mock;
8
+
9
+ /**
10
+ * Find the Mock client.
11
+ *
12
+ * @author Sam Rapaport <me@samrapdev.com>
13
+ */
14
+ final class MockClientStrategy implements DiscoveryStrategy
15
+ {
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ public static function getCandidates($type)
20
+ {
21
+ if (is_a(HttpClient::class, $type, true) || is_a(HttpAsyncClient::class, $type, true)) {
22
+ return [['class' => Mock::class, 'condition' => Mock::class]];
23
+ }
24
+
25
+ return [];
26
+ }
27
+ }
src/vendor/php-http/discovery/src/Strategy/PuliBetaStrategy.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery\Strategy;
4
+
5
+ use Http\Discovery\ClassDiscovery;
6
+ use Http\Discovery\Exception\PuliUnavailableException;
7
+ use Puli\Discovery\Api\Discovery;
8
+ use Puli\GeneratedPuliFactory;
9
+
10
+ /**
11
+ * Find candidates using Puli.
12
+ *
13
+ * @internal
14
+ * @final
15
+ *
16
+ * @author David de Boer <david@ddeboer.nl>
17
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
18
+ */
19
+ class PuliBetaStrategy implements DiscoveryStrategy
20
+ {
21
+ /**
22
+ * @var GeneratedPuliFactory
23
+ */
24
+ protected static $puliFactory;
25
+
26
+ /**
27
+ * @var Discovery
28
+ */
29
+ protected static $puliDiscovery;
30
+
31
+ /**
32
+ * @return GeneratedPuliFactory
33
+ *
34
+ * @throws PuliUnavailableException
35
+ */
36
+ private static function getPuliFactory()
37
+ {
38
+ if (null === self::$puliFactory) {
39
+ if (!defined('PULI_FACTORY_CLASS')) {
40
+ throw new PuliUnavailableException('Puli Factory is not available');
41
+ }
42
+
43
+ $puliFactoryClass = PULI_FACTORY_CLASS;
44
+
45
+ if (!ClassDiscovery::safeClassExists($puliFactoryClass)) {
46
+ throw new PuliUnavailableException('Puli Factory class does not exist');
47
+ }
48
+
49
+ self::$puliFactory = new $puliFactoryClass();
50
+ }
51
+
52
+ return self::$puliFactory;
53
+ }
54
+
55
+ /**
56
+ * Returns the Puli discovery layer.
57
+ *
58
+ * @return Discovery
59
+ *
60
+ * @throws PuliUnavailableException
61
+ */
62
+ private static function getPuliDiscovery()
63
+ {
64
+ if (!isset(self::$puliDiscovery)) {
65
+ $factory = self::getPuliFactory();
66
+ $repository = $factory->createRepository();
67
+
68
+ self::$puliDiscovery = $factory->createDiscovery($repository);
69
+ }
70
+
71
+ return self::$puliDiscovery;
72
+ }
73
+
74
+ /**
75
+ * {@inheritdoc}
76
+ */
77
+ public static function getCandidates($type)
78
+ {
79
+ $returnData = [];
80
+ $bindings = self::getPuliDiscovery()->findBindings($type);
81
+
82
+ foreach ($bindings as $binding) {
83
+ $condition = true;
84
+ if ($binding->hasParameterValue('depends')) {
85
+ $condition = $binding->getParameterValue('depends');
86
+ }
87
+ $returnData[] = ['class' => $binding->getClassName(), 'condition' => $condition];
88
+ }
89
+
90
+ return $returnData;
91
+ }
92
+ }
src/vendor/php-http/discovery/src/StreamFactoryDiscovery.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ use Http\Discovery\Exception\DiscoveryFailedException;
6
+ use Http\Message\StreamFactory;
7
+
8
+ /**
9
+ * Finds a Stream Factory.
10
+ *
11
+ * @author Михаил Красильников <m.krasilnikov@yandex.ru>
12
+ *
13
+ * @deprecated This will be removed in 2.0. Consider using Psr17FactoryDiscovery.
14
+ */
15
+ final class StreamFactoryDiscovery extends ClassDiscovery
16
+ {
17
+ /**
18
+ * Finds a Stream Factory.
19
+ *
20
+ * @return StreamFactory
21
+ *
22
+ * @throws Exception\NotFoundException
23
+ */
24
+ public static function find()
25
+ {
26
+ try {
27
+ $streamFactory = static::findOneByType(StreamFactory::class);
28
+ } catch (DiscoveryFailedException $e) {
29
+ throw new NotFoundException('No stream factories found. To use Guzzle, Diactoros or Slim Framework factories install php-http/message and the chosen message implementation.', 0, $e);
30
+ }
31
+
32
+ return static::instantiateClass($streamFactory);
33
+ }
34
+ }
src/vendor/php-http/discovery/src/UriFactoryDiscovery.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Discovery;
4
+
5
+ use Http\Discovery\Exception\DiscoveryFailedException;
6
+ use Http\Message\UriFactory;
7
+
8
+ /**
9
+ * Finds a URI Factory.
10
+ *
11
+ * @author David de Boer <david@ddeboer.nl>
12
+ *
13
+ * @deprecated This will be removed in 2.0. Consider using Psr17FactoryDiscovery.
14
+ */
15
+ final class UriFactoryDiscovery extends ClassDiscovery
16
+ {
17
+ /**
18
+ * Finds a URI Factory.
19
+ *
20
+ * @return UriFactory
21
+ *
22
+ * @throws Exception\NotFoundException
23
+ */
24
+ public static function find()
25
+ {
26
+ try {
27
+ $uriFactory = static::findOneByType(UriFactory::class);
28
+ } catch (DiscoveryFailedException $e) {
29
+ throw new NotFoundException('No uri factories found. To use Guzzle, Diactoros or Slim Framework factories install php-http/message and the chosen message implementation.', 0, $e);
30
+ }
31
+
32
+ return static::instantiateClass($uriFactory);
33
+ }
34
+ }
src/vendor/php-http/httplug/.php-cs-fixer.dist.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $finder = PhpCsFixer\Finder::create()
4
+ ->in(__DIR__.'/src')
5
+ ->name('*.php')
6
+ ;
7
+
8
+ $config = (new PhpCsFixer\Config())
9
+ ->setRiskyAllowed(true)
10
+ ->setRules([
11
+ '@Symfony' => true,
12
+ ])
13
+ ->setFinder($finder)
14
+ ;
15
+
16
+ return $config;
src/vendor/php-http/httplug/CHANGELOG.md ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Change Log
2
+
3
+
4
+ All notable changes to this project will be documented in this file.
5
+
6
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
7
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
8
+
9
+
10
+ ## [Unreleased]
11
+
12
+ ## [2.3.0] - 2022-02-21
13
+
14
+ ### Changed
15
+
16
+ - Enabled the `$onRejected` callback of `HttpRejectedPromise` to return a promise for implementing a retry
17
+ mechanism [#168](https://github.com/php-http/httplug/pull/168)
18
+
19
+ ## [2.2.0] - 2020-07-13
20
+
21
+ ### Changed
22
+
23
+ - Support PHP 7.1-8.0
24
+
25
+ ## [2.1.0] - 2019-12-27
26
+
27
+ ### Changed
28
+
29
+ - `Http\Client\Exception\NetworkException` no longer extends `Http\Client\Exception\RequestException`,
30
+ in accordance with [PSR-18](https://www.php-fig.org/psr/psr-18/)
31
+
32
+ ## [2.0.0] - 2018-10-31
33
+
34
+ This version is no BC break for consumers using HTTPlug. However, HTTP clients that
35
+ implement HTTPlug need to adjust because we add return type declarations.
36
+
37
+ ### Added
38
+
39
+ - Support for PSR-18 (HTTP client).
40
+
41
+ ### Changed
42
+
43
+ - **BC Break:** `HttpClient::sendRequest(RequestInterface $request)` has a return type annotation. The new
44
+ signature is `HttpClient::sendRequest(RequestInterface $request): ResponseInterface`.
45
+ - **BC Break:** `RequestException::getRequest()` has a return type annotation. The new
46
+ signature is `RequestException::getRequest(): RequestInterface`.
47
+
48
+ ### Removed
49
+
50
+ - PHP 5 support
51
+
52
+
53
+ ## [1.1.0] - 2016-08-31
54
+
55
+ ### Added
56
+
57
+ - HttpFulfilledPromise and HttpRejectedPromise which respect the HttpAsyncClient interface
58
+
59
+
60
+ ## [1.0.0] - 2016-01-26
61
+
62
+ ### Removed
63
+
64
+ - Stability configuration from composer
65
+
66
+
67
+ ## [1.0.0-RC1] - 2016-01-12
68
+
69
+ ### Changed
70
+
71
+ - Updated package files
72
+ - Updated promise dependency to RC1
73
+
74
+
75
+ ## [1.0.0-beta] - 2015-12-17
76
+
77
+ ### Added
78
+
79
+ - Puli configuration and binding types
80
+
81
+ ### Changed
82
+
83
+ - Exception concept
84
+
85
+
86
+ ## [1.0.0-alpha3] - 2015-12-13
87
+
88
+ ### Changed
89
+
90
+ - Async client does not throw exceptions
91
+
92
+ ### Removed
93
+
94
+ - Promise interface moved to its own repository: [php-http/promise](https://github.com/php-http/promise)
95
+
96
+
97
+ ## [1.0.0-alpha2] - 2015-11-16
98
+
99
+ ### Added
100
+
101
+ - Async client and Promise interface
102
+
103
+
104
+ ## [1.0.0-alpha] - 2015-10-26
105
+
106
+ ### Added
107
+
108
+ - Better domain exceptions.
109
+
110
+ ### Changed
111
+
112
+ - Purpose of the library: general HTTP CLient abstraction.
113
+
114
+ ### Removed
115
+
116
+ - Request options: they should be configured at construction time.
117
+ - Multiple request sending: should be done asynchronously using Async Client.
118
+ - `getName` method
119
+
120
+
121
+ ## 0.1.0 - 2015-06-03
122
+
123
+ ### Added
124
+
125
+ - Initial release
126
+
127
+
128
+ [Unreleased]: https://github.com/php-http/httplug/compare/v2.0.0...HEAD
129
+ [2.0.0]: https://github.com/php-http/httplug/compare/v1.1.0...HEAD
130
+ [1.1.0]: https://github.com/php-http/httplug/compare/v1.0.0...v1.1.0
131
+ [1.0.0]: https://github.com/php-http/httplug/compare/v1.0.0-RC1...v1.0.0
132
+ [1.0.0-RC1]: https://github.com/php-http/httplug/compare/v1.0.0-beta...v1.0.0-RC1
133
+ [1.0.0-beta]: https://github.com/php-http/httplug/compare/v1.0.0-alpha3...v1.0.0-beta
134
+ [1.0.0-alpha3]: https://github.com/php-http/httplug/compare/v1.0.0-alpha2...v1.0.0-alpha3
135
+ [1.0.0-alpha2]: https://github.com/php-http/httplug/compare/v1.0.0-alpha...v1.0.0-alpha2
136
+ [1.0.0-alpha]: https://github.com/php-http/httplug/compare/v0.1.0...v1.0.0-alpha
src/vendor/php-http/httplug/LICENSE ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2014 Eric GELOEN <geloen.eric@gmail.com>
2
+ Copyright (c) 2015 PHP HTTP Team <team@php-http.org>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
src/vendor/php-http/httplug/README.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HTTPlug
2
+
3
+ [![Latest Version](https://img.shields.io/github/release/php-http/httplug.svg?style=flat-square)](https://github.com/php-http/httplug/releases)
4
+ [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
5
+ [![Build Status](https://github.com/php-http/httplug/actions/workflows/ci.yml/badge.svg)](https://github.com/php-http/httplug/actions/workflows/ci.yml)
6
+ [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-http/httplug.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/httplug)
7
+ [![Quality Score](https://img.shields.io/scrutinizer/g/php-http/httplug.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-http/httplug)
8
+ [![Total Downloads](https://img.shields.io/packagist/dt/php-http/httplug.svg?style=flat-square)](https://packagist.org/packages/php-http/httplug)
9
+
10
+ [![Email](https://img.shields.io/badge/email-team@httplug.io-blue.svg?style=flat-square)](mailto:team@httplug.io)
11
+
12
+ **HTTPlug, the HTTP client abstraction for PHP.**
13
+
14
+
15
+ ## Intro
16
+
17
+ HTTP client standard built on [PSR-7](http://www.php-fig.org/psr/psr-7/) HTTP
18
+ messages. The HTTPlug client interface is compatible with the official standard
19
+ for the HTTP client interface, [PSR-18](http://www.php-fig.org/psr/psr-18/).
20
+ HTTPlug adds an interface for asynchronous HTTP requests, which PSR-18 does not
21
+ cover.
22
+
23
+ Since HTTPlug has already been widely adopted and a whole ecosystem has been
24
+ built around it, we will keep maintaining this package for the time being.
25
+ HTTPlug 2.0 and newer extend the PSR-18 interface to allow for a convenient
26
+ migration path.
27
+
28
+ New client implementations and consumers should use the PSR-18 interfaces
29
+ directly. In the long term, we expect PSR-18 to completely replace the need
30
+ for HTTPlug.
31
+
32
+
33
+ ## History
34
+
35
+ HTTPlug is the official successor of the [ivory http adapter](https://github.com/egeloen/ivory-http-adapter).
36
+ HTTPlug is a predecessor of [PSR-18](http://www.php-fig.org/psr/psr-18/)
37
+
38
+
39
+ ## Install
40
+
41
+ Via Composer
42
+
43
+ ``` bash
44
+ $ composer require php-http/httplug
45
+ ```
46
+
47
+
48
+ ## Documentation
49
+
50
+ Please see the [official documentation](http://docs.php-http.org).
51
+
52
+
53
+ ## Testing
54
+
55
+ ``` bash
56
+ $ composer test
57
+ ```
58
+
59
+
60
+ ## License
61
+
62
+ The MIT License (MIT). Please see [License File](LICENSE) for more information.
src/vendor/php-http/httplug/composer.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "php-http/httplug",
3
+ "description": "HTTPlug, the HTTP client abstraction for PHP",
4
+ "keywords": [
5
+ "http",
6
+ "client"
7
+ ],
8
+ "homepage": "http://httplug.io",
9
+ "license": "MIT",
10
+ "authors": [
11
+ {
12
+ "name": "Eric GELOEN",
13
+ "email": "geloen.eric@gmail.com"
14
+ },
15
+ {
16
+ "name": "Márk Sági-Kazár",
17
+ "email": "mark.sagikazar@gmail.com",
18
+ "homepage": "https://sagikazarmark.hu"
19
+ }
20
+ ],
21
+ "require": {
22
+ "php": "^7.1 || ^8.0",
23
+ "php-http/promise": "^1.1",
24
+ "psr/http-client": "^1.0",
25
+ "psr/http-message": "^1.0"
26
+ },
27
+ "require-dev": {
28
+ "friends-of-phpspec/phpspec-code-coverage": "^4.1",
29
+ "phpspec/phpspec": "^5.1 || ^6.0"
30
+ },
31
+ "extra": {
32
+ "branch-alias": {
33
+ "dev-master": "2.x-dev"
34
+ }
35
+ },
36
+ "autoload": {
37
+ "psr-4": {
38
+ "Http\\Client\\": "src/"
39
+ }
40
+ },
41
+ "scripts": {
42
+ "test": "vendor/bin/phpspec run",
43
+ "test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
44
+ }
45
+ }
src/vendor/php-http/httplug/puli.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "1.0",
3
+ "name": "php-http/httplug",
4
+ "binding-types": {
5
+ "Http\\Client\\HttpAsyncClient": {
6
+ "description": "Async HTTP Client"
7
+ },
8
+ "Http\\Client\\HttpClient": {
9
+ "description": "HTTP Client"
10
+ }
11
+ }
12
+ }
src/vendor/php-http/httplug/src/Exception.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client;
4
+
5
+ use Psr\Http\Client\ClientExceptionInterface as PsrClientException;
6
+
7
+ /**
8
+ * Every HTTP Client related Exception must implement this interface.
9
+ *
10
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
11
+ */
12
+ interface Exception extends PsrClientException
13
+ {
14
+ }
src/vendor/php-http/httplug/src/Exception/HttpException.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client\Exception;
4
+
5
+ use Psr\Http\Message\RequestInterface;
6
+ use Psr\Http\Message\ResponseInterface;
7
+
8
+ /**
9
+ * Thrown when a response was received but the request itself failed.
10
+ *
11
+ * In addition to the request, this exception always provides access to the response object.
12
+ *
13
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
14
+ */
15
+ class HttpException extends RequestException
16
+ {
17
+ /**
18
+ * @var ResponseInterface
19
+ */
20
+ protected $response;
21
+
22
+ /**
23
+ * @param string $message
24
+ */
25
+ public function __construct(
26
+ $message,
27
+ RequestInterface $request,
28
+ ResponseInterface $response,
29
+ \Exception $previous = null
30
+ ) {
31
+ parent::__construct($message, $request, $previous);
32
+
33
+ $this->response = $response;
34
+ $this->code = $response->getStatusCode();
35
+ }
36
+
37
+ /**
38
+ * Returns the response.
39
+ *
40
+ * @return ResponseInterface
41
+ */
42
+ public function getResponse()
43
+ {
44
+ return $this->response;
45
+ }
46
+
47
+ /**
48
+ * Factory method to create a new exception with a normalized error message.
49
+ */
50
+ public static function create(
51
+ RequestInterface $request,
52
+ ResponseInterface $response,
53
+ \Exception $previous = null
54
+ ) {
55
+ $message = sprintf(
56
+ '[url] %s [http method] %s [status code] %s [reason phrase] %s',
57
+ $request->getRequestTarget(),
58
+ $request->getMethod(),
59
+ $response->getStatusCode(),
60
+ $response->getReasonPhrase()
61
+ );
62
+
63
+ return new static($message, $request, $response, $previous);
64
+ }
65
+ }
src/vendor/php-http/httplug/src/Exception/NetworkException.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client\Exception;
4
+
5
+ use Psr\Http\Client\NetworkExceptionInterface as PsrNetworkException;
6
+ use Psr\Http\Message\RequestInterface;
7
+
8
+ /**
9
+ * Thrown when the request cannot be completed because of network issues.
10
+ *
11
+ * There is no response object as this exception is thrown when no response has been received.
12
+ *
13
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
14
+ */
15
+ class NetworkException extends TransferException implements PsrNetworkException
16
+ {
17
+ use RequestAwareTrait;
18
+
19
+ /**
20
+ * @param string $message
21
+ */
22
+ public function __construct($message, RequestInterface $request, \Exception $previous = null)
23
+ {
24
+ $this->setRequest($request);
25
+
26
+ parent::__construct($message, 0, $previous);
27
+ }
28
+ }
src/vendor/php-http/httplug/src/Exception/RequestAwareTrait.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client\Exception;
4
+
5
+ use Psr\Http\Message\RequestInterface;
6
+
7
+ trait RequestAwareTrait
8
+ {
9
+ /**
10
+ * @var RequestInterface
11
+ */
12
+ private $request;
13
+
14
+ private function setRequest(RequestInterface $request)
15
+ {
16
+ $this->request = $request;
17
+ }
18
+
19
+ /**
20
+ * {@inheritdoc}
21
+ */
22
+ public function getRequest(): RequestInterface
23
+ {
24
+ return $this->request;
25
+ }
26
+ }
src/vendor/php-http/httplug/src/Exception/RequestException.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client\Exception;
4
+
5
+ use Psr\Http\Client\RequestExceptionInterface as PsrRequestException;
6
+ use Psr\Http\Message\RequestInterface;
7
+
8
+ /**
9
+ * Exception for when a request failed, providing access to the failed request.
10
+ *
11
+ * This could be due to an invalid request, or one of the extending exceptions
12
+ * for network errors or HTTP error responses.
13
+ *
14
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
15
+ */
16
+ class RequestException extends TransferException implements PsrRequestException
17
+ {
18
+ use RequestAwareTrait;
19
+
20
+ /**
21
+ * @param string $message
22
+ */
23
+ public function __construct($message, RequestInterface $request, \Exception $previous = null)
24
+ {
25
+ $this->setRequest($request);
26
+
27
+ parent::__construct($message, 0, $previous);
28
+ }
29
+ }
src/vendor/php-http/httplug/src/Exception/TransferException.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client\Exception;
4
+
5
+ use Http\Client\Exception;
6
+
7
+ /**
8
+ * Base exception for transfer related exceptions.
9
+ *
10
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
11
+ */
12
+ class TransferException extends \RuntimeException implements Exception
13
+ {
14
+ }
src/vendor/php-http/httplug/src/HttpAsyncClient.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client;
4
+
5
+ use Http\Promise\Promise;
6
+ use Psr\Http\Message\RequestInterface;
7
+
8
+ /**
9
+ * Sends a PSR-7 Request in an asynchronous way by returning a Promise.
10
+ *
11
+ * @author Joel Wurtz <joel.wurtz@gmail.com>
12
+ */
13
+ interface HttpAsyncClient
14
+ {
15
+ /**
16
+ * Sends a PSR-7 request in an asynchronous way.
17
+ *
18
+ * Exceptions related to processing the request are available from the returned Promise.
19
+ *
20
+ * @return Promise resolves a PSR-7 Response or fails with an Http\Client\Exception
21
+ *
22
+ * @throws \Exception If processing the request is impossible (eg. bad configuration).
23
+ */
24
+ public function sendAsyncRequest(RequestInterface $request);
25
+ }
src/vendor/php-http/httplug/src/HttpClient.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client;
4
+
5
+ use Psr\Http\Client\ClientInterface;
6
+
7
+ /**
8
+ * {@inheritdoc}
9
+ *
10
+ * Provide the Httplug HttpClient interface for BC.
11
+ * You should typehint Psr\Http\Client\ClientInterface in new code
12
+ */
13
+ interface HttpClient extends ClientInterface
14
+ {
15
+ }
src/vendor/php-http/httplug/src/Promise/HttpFulfilledPromise.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client\Promise;
4
+
5
+ use Http\Client\Exception;
6
+ use Http\Promise\Promise;
7
+ use Psr\Http\Message\ResponseInterface;
8
+
9
+ final class HttpFulfilledPromise implements Promise
10
+ {
11
+ /**
12
+ * @var ResponseInterface
13
+ */
14
+ private $response;
15
+
16
+ public function __construct(ResponseInterface $response)
17
+ {
18
+ $this->response = $response;
19
+ }
20
+
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public function then(callable $onFulfilled = null, callable $onRejected = null)
25
+ {
26
+ if (null === $onFulfilled) {
27
+ return $this;
28
+ }
29
+
30
+ try {
31
+ return new self($onFulfilled($this->response));
32
+ } catch (Exception $e) {
33
+ return new HttpRejectedPromise($e);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * {@inheritdoc}
39
+ */
40
+ public function getState()
41
+ {
42
+ return Promise::FULFILLED;
43
+ }
44
+
45
+ /**
46
+ * {@inheritdoc}
47
+ */
48
+ public function wait($unwrap = true)
49
+ {
50
+ if ($unwrap) {
51
+ return $this->response;
52
+ }
53
+ }
54
+ }
src/vendor/php-http/httplug/src/Promise/HttpRejectedPromise.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Client\Promise;
4
+
5
+ use Http\Client\Exception;
6
+ use Http\Promise\Promise;
7
+
8
+ final class HttpRejectedPromise implements Promise
9
+ {
10
+ /**
11
+ * @var Exception
12
+ */
13
+ private $exception;
14
+
15
+ public function __construct(Exception $exception)
16
+ {
17
+ $this->exception = $exception;
18
+ }
19
+
20
+ /**
21
+ * {@inheritdoc}
22
+ */
23
+ public function then(callable $onFulfilled = null, callable $onRejected = null)
24
+ {
25
+ if (null === $onRejected) {
26
+ return $this;
27
+ }
28
+
29
+ try {
30
+ $result = $onRejected($this->exception);
31
+ if ($result instanceof Promise) {
32
+ return $result;
33
+ }
34
+
35
+ return new HttpFulfilledPromise($result);
36
+ } catch (Exception $e) {
37
+ return new self($e);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * {@inheritdoc}
43
+ */
44
+ public function getState()
45
+ {
46
+ return Promise::REJECTED;
47
+ }
48
+
49
+ /**
50
+ * {@inheritdoc}
51
+ */
52
+ public function wait($unwrap = true)
53
+ {
54
+ if ($unwrap) {
55
+ throw $this->exception;
56
+ }
57
+ }
58
+ }
src/vendor/php-http/message-factory/CHANGELOG.md ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Change Log
2
+
3
+
4
+ ## 1.0.2 - 2015-12-19
5
+
6
+ ### Added
7
+
8
+ - Request and Response factory binding types to Puli
9
+
10
+
11
+ ## 1.0.1 - 2015-12-17
12
+
13
+ ### Added
14
+
15
+ - Puli configuration and binding types
16
+
17
+
18
+ ## 1.0.0 - 2015-12-15
19
+
20
+ ### Added
21
+
22
+ - Response Factory in order to be reused in Message and Server Message factories
23
+ - Request Factory
24
+
25
+ ### Changed
26
+
27
+ - Message Factory extends Request and Response factories
28
+
29
+
30
+ ## 1.0.0-RC1 - 2015-12-14
31
+
32
+ ### Added
33
+
34
+ - CS check
35
+
36
+ ### Changed
37
+
38
+ - RuntimeException is thrown when the StreamFactory cannot write to the underlying stream
39
+
40
+
41
+ ## 0.3.0 - 2015-11-16
42
+
43
+ ### Removed
44
+
45
+ - Client Context Factory
46
+ - Factory Awares and Templates
47
+
48
+
49
+ ## 0.2.0 - 2015-11-16
50
+
51
+ ### Changed
52
+
53
+ - Reordered the parameters when creating a message to have the protocol last,
54
+ as its the least likely to need to be changed.
55
+
56
+
57
+ ## 0.1.0 - 2015-06-01
58
+
59
+ ### Added
60
+
61
+ - Initial release
62
+
63
+ ### Changed
64
+
65
+ - Helpers are renamed to templates
src/vendor/php-http/message-factory/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2015 PHP HTTP Team <team@php-http.org>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
src/vendor/php-http/message-factory/README.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PSR-7 Message Factory
2
+
3
+ [![Latest Version](https://img.shields.io/github/release/php-http/message-factory.svg?style=flat-square)](https://github.com/php-http/message-factory/releases)
4
+ [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
5
+ [![Total Downloads](https://img.shields.io/packagist/dt/php-http/message-factory.svg?style=flat-square)](https://packagist.org/packages/php-http/message-factory)
6
+
7
+ **Factory interfaces for PSR-7 HTTP Message.**
8
+
9
+
10
+ ## Install
11
+
12
+ Via Composer
13
+
14
+ ``` bash
15
+ $ composer require php-http/message-factory
16
+ ```
17
+
18
+
19
+ ## Documentation
20
+
21
+ Please see the [official documentation](http://php-http.readthedocs.org/en/latest/message-factory/).
22
+
23
+
24
+ ## Contributing
25
+
26
+ Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details.
27
+
28
+
29
+ ## Security
30
+
31
+ If you discover any security related issues, please contact us at [security@php-http.org](mailto:security@php-http.org).
32
+
33
+
34
+ ## License
35
+
36
+ The MIT License (MIT). Please see [License File](LICENSE) for more information.
src/vendor/php-http/message-factory/composer.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "php-http/message-factory",
3
+ "description": "Factory interfaces for PSR-7 HTTP Message",
4
+ "license": "MIT",
5
+ "keywords": ["http", "factory", "message", "stream", "uri"],
6
+ "homepage": "http://php-http.org",
7
+ "authors": [
8
+ {
9
+ "name": "Márk Sági-Kazár",
10
+ "email": "mark.sagikazar@gmail.com"
11
+ }
12
+ ],
13
+ "require": {
14
+ "php": ">=5.4",
15
+ "psr/http-message": "^1.0"
16
+ },
17
+ "autoload": {
18
+ "psr-4": {
19
+ "Http\\Message\\": "src/"
20
+ }
21
+ },
22
+ "extra": {
23
+ "branch-alias": {
24
+ "dev-master": "1.0-dev"
25
+ }
26
+ }
27
+ }
src/vendor/php-http/message-factory/puli.json ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "1.0",
3
+ "binding-types": {
4
+ "Http\\Message\\MessageFactory": {
5
+ "description": "PSR-7 Message Factory",
6
+ "parameters": {
7
+ "depends": {
8
+ "description": "Optional class dependency which can be checked by consumers"
9
+ }
10
+ }
11
+ },
12
+ "Http\\Message\\RequestFactory": {
13
+ "parameters": {
14
+ "depends": {
15
+ "description": "Optional class dependency which can be checked by consumers"
16
+ }
17
+ }
18
+ },
19
+ "Http\\Message\\ResponseFactory": {
20
+ "parameters": {
21
+ "depends": {
22
+ "description": "Optional class dependency which can be checked by consumers"
23
+ }
24
+ }
25
+ },
26
+ "Http\\Message\\StreamFactory": {
27
+ "description": "PSR-7 Stream Factory",
28
+ "parameters": {
29
+ "depends": {
30
+ "description": "Optional class dependency which can be checked by consumers"
31
+ }
32
+ }
33
+ },
34
+ "Http\\Message\\UriFactory": {
35
+ "description": "PSR-7 URI Factory",
36
+ "parameters": {
37
+ "depends": {
38
+ "description": "Optional class dependency which can be checked by consumers"
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
src/vendor/php-http/message-factory/src/MessageFactory.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Message;
4
+
5
+ /**
6
+ * Factory for PSR-7 Request and Response.
7
+ *
8
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
9
+ */
10
+ interface MessageFactory extends RequestFactory, ResponseFactory
11
+ {
12
+ }
src/vendor/php-http/message-factory/src/RequestFactory.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Message;
4
+
5
+ use Psr\Http\Message\UriInterface;
6
+ use Psr\Http\Message\RequestInterface;
7
+ use Psr\Http\Message\StreamInterface;
8
+
9
+ /**
10
+ * Factory for PSR-7 Request.
11
+ *
12
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
13
+ */
14
+ interface RequestFactory
15
+ {
16
+ /**
17
+ * Creates a new PSR-7 request.
18
+ *
19
+ * @param string $method
20
+ * @param string|UriInterface $uri
21
+ * @param array $headers
22
+ * @param resource|string|StreamInterface|null $body
23
+ * @param string $protocolVersion
24
+ *
25
+ * @return RequestInterface
26
+ */
27
+ public function createRequest(
28
+ $method,
29
+ $uri,
30
+ array $headers = [],
31
+ $body = null,
32
+ $protocolVersion = '1.1'
33
+ );
34
+ }
src/vendor/php-http/message-factory/src/ResponseFactory.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Message;
4
+
5
+ use Psr\Http\Message\ResponseInterface;
6
+ use Psr\Http\Message\StreamInterface;
7
+
8
+ /**
9
+ * Factory for PSR-7 Response.
10
+ *
11
+ * This factory contract can be reused in Message and Server Message factories.
12
+ *
13
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
14
+ */
15
+ interface ResponseFactory
16
+ {
17
+ /**
18
+ * Creates a new PSR-7 response.
19
+ *
20
+ * @param int $statusCode
21
+ * @param string|null $reasonPhrase
22
+ * @param array $headers
23
+ * @param resource|string|StreamInterface|null $body
24
+ * @param string $protocolVersion
25
+ *
26
+ * @return ResponseInterface
27
+ */
28
+ public function createResponse(
29
+ $statusCode = 200,
30
+ $reasonPhrase = null,
31
+ array $headers = [],
32
+ $body = null,
33
+ $protocolVersion = '1.1'
34
+ );
35
+ }
src/vendor/php-http/message-factory/src/StreamFactory.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Message;
4
+
5
+ use Psr\Http\Message\StreamInterface;
6
+
7
+ /**
8
+ * Factory for PSR-7 Stream.
9
+ *
10
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
11
+ */
12
+ interface StreamFactory
13
+ {
14
+ /**
15
+ * Creates a new PSR-7 stream.
16
+ *
17
+ * @param string|resource|StreamInterface|null $body
18
+ *
19
+ * @return StreamInterface
20
+ *
21
+ * @throws \InvalidArgumentException If the stream body is invalid.
22
+ * @throws \RuntimeException If creating the stream from $body fails.
23
+ */
24
+ public function createStream($body = null);
25
+ }
src/vendor/php-http/message-factory/src/UriFactory.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Http\Message;
4
+
5
+ use Psr\Http\Message\UriInterface;
6
+
7
+ /**
8
+ * Factory for PSR-7 URI.
9
+ *
10
+ * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
11
+ */
12
+ interface UriFactory
13
+ {
14
+ /**
15
+ * Creates an PSR-7 URI.
16
+ *
17
+ * @param string|UriInterface $uri
18
+ *
19
+ * @return UriInterface
20
+ *
21
+ * @throws \InvalidArgumentException If the $uri argument can not be converted into a valid URI.
22
+ */
23
+ public function createUri($uri);
24
+ }
src/vendor/php-http/message/CHANGELOG.md ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Change Log
2
+
3
+
4
+ All notable changes to this project will be documented in this file.
5
+
6
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
7
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
8
+
9
+ ## [1.13.0] - 2022-02-11
10
+
11
+ - Added `Formatter::formatResponseForRequest()` to allow the formatter to get context from the request to decide what of the response to output.
12
+ - Deprecated `Formatter::formatResponse()` in favor of the new `formatResponseForRequest` method.
13
+
14
+ ## [1.12.0] - 2021-08-29
15
+
16
+ - Added support for adjusting binary detection regex in FullHttpMessageFormatter.
17
+
18
+ ## [1.11.2] - 2021-08-03
19
+
20
+ - Support GuzzleHttp/Psr7 version 2.0 in the (deprecated) GuzzleStreamFactory.
21
+
22
+ ## [1.11.1] - 2021-05-24
23
+
24
+ - Support GuzzleHttp/Psr7 version 2.0 in the (deprecated) GuzzleUriFactory.
25
+
26
+ ## [1.11.0] - 2020-02-01
27
+
28
+ - Migrated from `zendframework/zend-diactoros` to `laminas/laminas-diactoros`.
29
+ Users are encouraged to update their dependencies by simply replacing the Zend package with the Laminas package.
30
+ Due to the [laminas-zendframework-brige](https://github.com/laminas/laminas-zendframework-bridge), BC changes
31
+ are not expected and legacy code does not need to be refactored (though it is
32
+ [recommended and simple](https://docs.laminas.dev/migration/)).
33
+ - The diactoros factories of `php-http/message` will return objects from the `Laminas\Diactoros\` namespace, if
34
+ the respective classes are available via autoloading, but continue to return objects from `Zend\Diactoros\`
35
+ namespace otherwise.
36
+
37
+ - Allow to specify the hashing algorithm for WSSE authentication.
38
+
39
+ ## [1.10.0] - 2020-11-11
40
+
41
+ - Added support for PHP 8.0.
42
+
43
+ ## [1.9.1] - 2020-10-13
44
+
45
+ - Improved detection of binary stream to not consider newlines, carriage return or tabs as binary.
46
+
47
+ ## [1.9.0] - 2020-08-17
48
+
49
+ - Omitted binary body in FullHttpMessageFormatter and CurlCommandFormatter.
50
+ `[binary stream omitted]` will be shown instead.
51
+
52
+ ### Added
53
+
54
+ - New Header authentication method for arbitrary header authentication.
55
+
56
+ ## [1.8.0] - 2019-08-05
57
+
58
+ ### Changed
59
+
60
+ - Raised minimum PHP version to 7.1
61
+
62
+ ### Fixed
63
+
64
+ - Fatal error on `CurlCommandFormatter` when body is larger than `escapeshellarg` allowed length.
65
+ - Do not read stream in message formatter if stream is not seekable.
66
+
67
+ ## [1.7.2] - 2018-10-30
68
+
69
+ ### Fixed
70
+
71
+ - FilteredStream uses `@trigger_error` instead of throwing exceptions to not
72
+ break careless users. You still need to fix your stream code to respect
73
+ `isSeekable`. Seeking does not work as expected, and we will add exceptions
74
+ in version 2.
75
+
76
+ ## [1.7.1] - 2018-10-29
77
+
78
+ ### Fixed
79
+
80
+ - FilteredStream is not actually seekable
81
+
82
+
83
+ ## [1.7.0] - 2018-08-15
84
+
85
+ ### Fixed
86
+
87
+ - Fix CurlCommandFormatter for binary request payloads
88
+ - Fix QueryParam authentication to assemble proper URL regardless of PHP `arg_separator.output` directive
89
+ - Do not pass `null` parameters to `Clue\StreamFilter\fun`
90
+
91
+ ### Changed
92
+
93
+ - Dropped tests on HHVM
94
+
95
+
96
+ ## [1.6.0] - 2017-07-05
97
+
98
+ ### Added
99
+
100
+ - CookieUtil::parseDate to create a date from cookie date string
101
+
102
+ ### Fixed
103
+
104
+ - Fix curl command of CurlFormatter when there is an user-agent header
105
+
106
+
107
+ ## [1.5.0] - 2017-02-14
108
+
109
+ ### Added
110
+
111
+ - Check for empty string in Stream factories
112
+ - Cookie::createWithoutValidation Static constructor to create a cookie. Will not perform any attribute validation during instantiation.
113
+ - Cookie::isValid Method to check if cookie attributes are valid.
114
+
115
+ ### Fixed
116
+
117
+ - FilteredStream::getSize returns null because the contents size is unknown.
118
+ - Stream factories does not rewinds streams. The previous behavior was not coherent between factories and inputs.
119
+
120
+ ### Deprecated
121
+
122
+ - FilteredStream::getReadFilter The read filter is internal and should never be used by consuming code.
123
+ - FilteredStream::getWriteFilter We did not implement writing to the streams at all. And if we do, the filter is an internal information and should not be used by consuming code.
124
+
125
+
126
+ ## [1.4.1] - 2016-12-16
127
+
128
+ ### Fixed
129
+
130
+ - Cookie::matchPath Cookie with root path (`/`) will not match sub path (e.g. `/cookie`).
131
+
132
+
133
+ ## [1.4.0] - 2016-10-20
134
+
135
+ ### Added
136
+
137
+ - Message, stream and URI factories for [Slim Framework](https://github.com/slimphp/Slim)
138
+ - BufferedStream that allow you to decorate a non-seekable stream with a seekable one.
139
+ - cUrlFormatter to be able to redo the request with a cURL command
140
+
141
+
142
+ ## [1.3.1] - 2016-07-15
143
+
144
+ ### Fixed
145
+
146
+ - FullHttpMessageFormatter will not read from streams that you cannot rewind (non-seekable)
147
+ - FullHttpMessageFormatter will not read from the stream if $maxBodyLength is zero
148
+ - FullHttpMessageFormatter rewinds streams after they are read
149
+
150
+
151
+ ## [1.3.0] - 2016-07-14
152
+
153
+ ### Added
154
+
155
+ - FullHttpMessageFormatter to include headers and body in the formatted message
156
+
157
+ ### Fixed
158
+
159
+ - #41: Response builder broke header value
160
+
161
+
162
+ ## [1.2.0] - 2016-03-29
163
+
164
+ ### Added
165
+
166
+ - The RequestMatcher is built after the Symfony RequestMatcher and separates
167
+ scheme, host and path expressions and provides an option to filter on the
168
+ method
169
+ - New RequestConditional authentication method using request matchers
170
+ - Add automatic basic auth info detection based on the URL
171
+
172
+ ### Changed
173
+
174
+ - Improved ResponseBuilder
175
+
176
+ ### Deprecated
177
+
178
+ - RegexRequestMatcher, use RequestMatcher instead
179
+ - Matching authenitcation method, use RequestConditional instead
180
+
181
+
182
+ ## [1.1.0] - 2016-02-25
183
+
184
+ ### Added
185
+
186
+ - Add a request matcher interface and regex implementation
187
+ - Add a callback request matcher implementation
188
+ - Add a ResponseBuilder, to create PSR7 Response from a string
189
+
190
+ ### Fixed
191
+
192
+ - Fix casting string on a FilteredStream not filtering the output
193
+
194
+
195
+ ## [1.0.0] - 2016-01-27
196
+
197
+
198
+ ## [0.2.0] - 2015-12-29
199
+
200
+ ### Added
201
+
202
+ - Autoregistration of stream filters using Composer autoload
203
+ - Cookie
204
+ - [Apigen](http://www.apigen.org/) configuration
205
+
206
+
207
+ ## [0.1.2] - 2015-12-26
208
+
209
+ ### Added
210
+
211
+ - Request and response factory bindings
212
+
213
+ ### Fixed
214
+
215
+ - Chunk filter namespace in Dechunk stream
216
+
217
+
218
+ ## [0.1.1] - 2015-12-25
219
+
220
+ ### Added
221
+
222
+ - Formatter
223
+
224
+
225
+ ## 0.1.0 - 2015-12-24
226
+
227
+ ### Added
228
+
229
+ - Authentication
230
+ - Encoding
231
+ - Message decorator
232
+ - Message factory (Guzzle, Diactoros)
233
+
234
+
235
+ [Unreleased]: https://github.com/php-http/message/compare/1.10.0...HEAD
236
+ [1.10.0]: https://github.com/php-http/message/compare/1.9.1...1.10.0
237
+ [1.9.1]: https://github.com/php-http/message/compare/1.9.0...1.9.1
238
+ [1.9.0]: https://github.com/php-http/message/compare/1.8.0...1.9.0
239
+ [1.8.0]: https://github.com/php-http/message/compare/1.7.2...1.8.0
240
+ [1.7.2]: https://github.com/php-http/message/compare/v1.7.1...1.7.2
241
+ [1.7.1]: https://github.com/php-http/message/compare/1.7.0...v1.7.1
242
+ [1.7.0]: https://github.com/php-http/message/compare/1.6.0...1.7.0
243
+ [1.6.0]: https://github.com/php-http/message/compare/1.5.0...1.6.0
244
+ [1.5.0]: https://github.com/php-http/message/compare/v1.4.1...1.5.0
245
+ [1.4.1]: https://github.com/php-http/message/compare/v1.4.0...v1.4.1
246
+ [1.4.0]: https://github.com/php-http/message/compare/v1.3.1...v1.4.0
247
+ [1.3.1]: https://github.com/php-http/message/compare/v1.3.0...v1.3.1
248
+ [1.3.0]: https://github.com/php-http/message/compare/v1.2.0...v1.3.0
249
+ [1.2.0]: https://github.com/php-http/message/compare/v1.1.0...v1.2.0
250
+ [1.1.0]: https://github.com/php-http/message/compare/v1.0.0...v1.1.0
251
+ [1.0.0]: https://github.com/php-http/message/compare/0.2.0...v1.0.0
252
+ [0.2.0]: https://github.com/php-http/message/compare/v0.1.2...0.2.0
253
+ [0.1.2]: https://github.com/php-http/message/compare/v0.1.1...v0.1.2
254
+ [0.1.1]: https://github.com/php-http/message/compare/v0.1.0...v0.1.1
src/vendor/php-http/message/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2015-2016 PHP HTTP Team <team@php-http.org>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
src/vendor/php-http/message/README.md ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HTTP Message
2
+
3
+ [![Latest Version](https://img.shields.io/github/release/php-http/message.svg?style=flat-square)](https://github.com/php-http/message/releases)
4
+ [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
5
+ [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/php-http/message/CI?style=flat-square)](https://github.com/php-http/message/actions?query=workflow%3ACI+branch%3Amaster)
6
+ [![Total Downloads](https://img.shields.io/packagist/dt/php-http/message.svg?style=flat-square)](https://packagist.org/packages/php-http/message)
7
+
8
+ **HTTP Message related tools.**
9
+
10
+
11
+ ## Install
12
+
13
+ Via Composer
14
+
15
+ ``` bash
16
+ $ composer require php-http/message
17
+ ```
18
+
19
+
20
+ ## Intro
21
+
22
+ This package contains various PSR-7 tools which might be useful in an HTTP workflow:
23
+
24
+ - Authentication method implementations
25
+ - Various Stream encoding tools
26
+ - Message decorators
27
+ - Message factory implementations for Guzzle PSR-7 and Diactoros
28
+ - Cookie implementation
29
+ - Request matchers
30
+
31
+
32
+ ## Documentation
33
+
34
+ Please see the [official documentation](http://docs.php-http.org/en/latest/message.html).
35
+
36
+
37
+ ## Testing
38
+
39
+ ``` bash
40
+ $ composer test
41
+ ```
42
+
43
+
44
+ ## Credits
45
+
46
+ Thanks to [Cuzzle](https://github.com/namshi/cuzzle) for inpiration for the `CurlCommandFormatter`.
47
+
48
+
49
+ ## License
50
+
51
+ The MIT License (MIT). Please see [License File](LICENSE) for more information.
src/vendor/php-http/message/apigen.neon ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ source:
2
+ - src/
3
+
4
+ destination: build/api/
5
+
6
+ templateTheme: bootstrap
src/vendor/php-http/message/composer.json ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "php-http/message",
3
+ "description": "HTTP Message related tools",
4
+ "keywords": [
5
+ "message",
6
+ "http",
7
+ "psr-7"
8
+ ],
9
+ "homepage": "http://php-http.org",
10
+ "license": "MIT",
11
+ "authors": [
12
+ {
13
+ "name": "Márk Sági-Kazár",
14
+ "email": "mark.sagikazar@gmail.com"
15
+ }
16
+ ],
17
+ "require": {
18
+ "php": "^7.1 || ^8.0",
19
+ "clue/stream-filter": "^1.5",
20
+ "php-http/message-factory": "^1.0.2",
21
+ "psr/http-message": "^1.0"
22
+ },
23
+ "provide": {
24
+ "php-http/message-factory-implementation": "1.0"
25
+ },
26
+ "require-dev": {
27
+ "ext-zlib": "*",
28
+ "ergebnis/composer-normalize": "^2.6",
29
+ "guzzlehttp/psr7": "^1.0",
30
+ "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
31
+ "slim/slim": "^3.0",
32
+ "laminas/laminas-diactoros": "^2.0"
33
+ },
34
+ "suggest": {
35
+ "ext-zlib": "Used with compressor/decompressor streams",
36
+ "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories",
37
+ "laminas/laminas-diactoros": "Used with Diactoros Factories",
38
+ "slim/slim": "Used with Slim Framework PSR-7 implementation"
39
+ },
40
+ "config": {
41
+ "sort-packages": true,
42
+ "allow-plugins": {
43
+ "ergebnis/composer-normalize": true
44
+ }
45
+ },
46
+ "extra": {
47
+ "branch-alias": {
48
+ "dev-master": "1.10-dev"
49
+ }
50
+ },
51
+ "autoload": {
52
+ "psr-4": {
53
+ "Http\\Message\\": "src/"
54
+ },
55
+ "files": [
56
+ "src/filters.php"
57
+ ]
58
+ },
59
+ "autoload-dev": {
60
+ "psr-4": {
61
+ "spec\\Http\\Message\\": "spec/"
62
+ }
63
+ },
64
+ "scripts": {
65
+ "test": "vendor/bin/phpspec run",
66
+ "test-ci": "vendor/bin/phpspec run -c phpspec.ci.yml"
67
+ }
68
+ }
src/vendor/php-http/message/puli.json ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "1.0",
3
+ "name": "php-http/message",
4
+ "bindings": {
5
+ "064d003d-78a1-48c4-8f3b-1f92ff25da69": {
6
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
7
+ "class": "Http\\Message\\MessageFactory\\DiactorosMessageFactory",
8
+ "type": "Http\\Message\\MessageFactory",
9
+ "parameters": {
10
+ "depends": "Zend\\Diactoros\\Request"
11
+ }
12
+ },
13
+ "0836751e-6558-4d1b-8993-4a52012947c3": {
14
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
15
+ "class": "Http\\Message\\MessageFactory\\SlimMessageFactory",
16
+ "type": "Http\\Message\\ResponseFactory"
17
+ },
18
+ "1d127622-dc61-4bfa-b9da-d221548d72c3": {
19
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
20
+ "class": "Http\\Message\\MessageFactory\\SlimMessageFactory",
21
+ "type": "Http\\Message\\RequestFactory"
22
+ },
23
+ "2438c2d0-0658-441f-8855-ddaf0f87d54d": {
24
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
25
+ "class": "Http\\Message\\MessageFactory\\GuzzleMessageFactory",
26
+ "type": "Http\\Message\\MessageFactory",
27
+ "parameters": {
28
+ "depends": "GuzzleHttp\\Psr7\\Request"
29
+ }
30
+ },
31
+ "253aa08c-d705-46e7-b1d2-e28c97eef792": {
32
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
33
+ "class": "Http\\Message\\MessageFactory\\GuzzleMessageFactory",
34
+ "type": "Http\\Message\\RequestFactory",
35
+ "parameters": {
36
+ "depends": "GuzzleHttp\\Psr7\\Request"
37
+ }
38
+ },
39
+ "273a34f9-62f4-4ba1-9801-b1284d49ff89": {
40
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
41
+ "class": "Http\\Message\\StreamFactory\\GuzzleStreamFactory",
42
+ "type": "Http\\Message\\StreamFactory",
43
+ "parameters": {
44
+ "depends": "GuzzleHttp\\Psr7\\Stream"
45
+ }
46
+ },
47
+ "304b83db-b594-4d83-ae75-1f633adf92f7": {
48
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
49
+ "class": "Http\\Message\\UriFactory\\GuzzleUriFactory",
50
+ "type": "Http\\Message\\UriFactory",
51
+ "parameters": {
52
+ "depends": "GuzzleHttp\\Psr7\\Uri"
53
+ }
54
+ },
55
+ "3f4bc1cd-aa95-4702-9fa7-65408e471691": {
56
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
57
+ "class": "Http\\Message\\UriFactory\\DiactorosUriFactory",
58
+ "type": "Http\\Message\\UriFactory",
59
+ "parameters": {
60
+ "depends": "Zend\\Diactoros\\Uri"
61
+ }
62
+ },
63
+ "4672a6ee-ad9e-4109-a5d1-b7d46f26c7a1": {
64
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
65
+ "class": "Http\\Message\\MessageFactory\\SlimMessageFactory",
66
+ "type": "Http\\Message\\MessageFactory"
67
+ },
68
+ "6234e947-d3bd-43eb-97d5-7f9e22e6bb1b": {
69
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
70
+ "class": "Http\\Message\\MessageFactory\\DiactorosMessageFactory",
71
+ "type": "Http\\Message\\ResponseFactory",
72
+ "parameters": {
73
+ "depends": "Zend\\Diactoros\\Response"
74
+ }
75
+ },
76
+ "6a9ad6ce-d82c-470f-8e30-60f21d9d95bf": {
77
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
78
+ "class": "Http\\Message\\UriFactory\\SlimUriFactory",
79
+ "type": "Http\\Message\\UriFactory"
80
+ },
81
+ "72c2afa0-ea56-4d03-adb6-a9f241a8a734": {
82
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
83
+ "class": "Http\\Message\\StreamFactory\\SlimStreamFactory",
84
+ "type": "Http\\Message\\StreamFactory"
85
+ },
86
+ "95c1be8f-39fe-4abd-8351-92cb14379a75": {
87
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
88
+ "class": "Http\\Message\\StreamFactory\\DiactorosStreamFactory",
89
+ "type": "Http\\Message\\StreamFactory",
90
+ "parameters": {
91
+ "depends": "Zend\\Diactoros\\Stream"
92
+ }
93
+ },
94
+ "a018af27-7590-4dcf-83a1-497f95604cd6": {
95
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
96
+ "class": "Http\\Message\\MessageFactory\\