Call Now Button - Version 1.1.11

Version Description

  • Welcome page for new installations
  • UI improvements
  • Bugfixes
Download this release

Release Info

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

Code changes from version 1.1.10 to 1.1.11

Files changed (192) hide show
  1. call-now-button.php +2 -2
  2. readme.txt +6 -1
  3. resources/images/button-scheduler.png +0 -0
  4. resources/images/buttonbar.png +0 -0
  5. resources/images/cnb-icons-actions.png +0 -0
  6. resources/images/icon-256x256.png +0 -0
  7. resources/images/multibutton.png +0 -0
  8. resources/images/whatsapp-modal.png +0 -0
  9. resources/js/action-type-to-icon-text.js +5 -4
  10. resources/js/call-now-button.js +2 -2
  11. resources/js/deactivation.js +6 -4
  12. resources/js/premium-activation.js +110 -0
  13. resources/js/preview.js +14 -5
  14. resources/js/settings.js +7 -106
  15. resources/style/call-now-button.css +124 -0
  16. src/CallNowButton.php +40 -6
  17. src/admin/CnbAdminAjax.php +33 -20
  18. src/admin/api/CnbDeleteResult.php +4 -3
  19. src/admin/button/CnbButtonView.php +3 -1
  20. src/admin/deactivation/Activation.php +55 -3
  21. src/admin/domain/CnbDomainViewEdit.php +1 -1
  22. src/admin/domain/partials/CnbDomainViewUpgradeOverview.php +5 -5
  23. src/admin/getting-started/class-getting-started-controller.php +13 -0
  24. src/admin/getting-started/class-getting-started-router.php +14 -0
  25. src/admin/getting-started/class-getting-started-view.php +115 -0
  26. src/admin/getting-started/index.php +1 -0
  27. src/admin/legacy/CnbLegacyController.php +0 -9
  28. src/admin/legacy/CnbLegacyEdit.php +118 -84
  29. src/admin/legacy/CnbLegacyUpgrade.php +21 -16
  30. src/admin/partials/CnbFooter.php +4 -4
  31. src/admin/partials/CnbHeaderNotices.php +19 -14
  32. src/admin/settings/CnbApiKeyActivatedView.php +6 -6
  33. src/admin/settings/CnbSettingsController.php +23 -6
  34. src/admin/settings/CnbSettingsViewEdit.php +15 -24
  35. src/autoload.php +3 -0
  36. src/composer.json +0 -6
  37. src/composer.lock +0 -1884
  38. src/utils/CnbUtils.php +5 -0
  39. src/utils/class-cnb-sentry.php +3 -14
  40. src/vendor/composer/InstalledVersions.php +7 -7
  41. src/vendor/composer/autoload_files.php +1 -1
  42. src/vendor/composer/autoload_psr4.php +3 -7
  43. src/vendor/composer/autoload_static.php +10 -30
  44. src/vendor/composer/installed.json +98 -454
  45. src/vendor/composer/installed.php +43 -95
  46. src/vendor/composer/platform_check.php +2 -2
  47. src/vendor/guzzlehttp/psr7/CHANGELOG.md +11 -4
  48. src/vendor/guzzlehttp/psr7/README.md +17 -3
  49. src/vendor/guzzlehttp/psr7/composer.json +1 -1
  50. src/vendor/guzzlehttp/psr7/src/CachingStream.php +5 -0
  51. src/vendor/guzzlehttp/psr7/src/DroppingStream.php +3 -0
  52. src/vendor/guzzlehttp/psr7/src/FnStream.php +1 -0
  53. src/vendor/guzzlehttp/psr7/src/InflateStream.php +3 -0
  54. src/vendor/guzzlehttp/psr7/src/LazyOpenStream.php +1 -0
  55. src/vendor/guzzlehttp/psr7/src/LimitStream.php +3 -0
  56. src/vendor/guzzlehttp/psr7/src/MultipartStream.php +3 -0
  57. src/vendor/guzzlehttp/psr7/src/NoSeekStream.php +3 -0
  58. src/vendor/guzzlehttp/psr7/src/UriComparator.php +52 -0
  59. src/vendor/http-interop/http-factory-guzzle/.github/workflows/ci.yaml +0 -60
  60. src/vendor/http-interop/http-factory-guzzle/LICENSE +0 -21
  61. src/vendor/http-interop/http-factory-guzzle/README.md +0 -7
  62. src/vendor/http-interop/http-factory-guzzle/composer.json +0 -37
  63. src/vendor/http-interop/http-factory-guzzle/src/RequestFactory.php +0 -15
  64. src/vendor/http-interop/http-factory-guzzle/src/ResponseFactory.php +0 -15
  65. src/vendor/http-interop/http-factory-guzzle/src/ServerRequestFactory.php +0 -24
  66. src/vendor/http-interop/http-factory-guzzle/src/StreamFactory.php +0 -26
  67. src/vendor/http-interop/http-factory-guzzle/src/UploadedFileFactory.php +0 -25
  68. src/vendor/http-interop/http-factory-guzzle/src/UriFactory.php +0 -15
  69. src/vendor/php-http/curl-client/.github/workflows/Build-Test.yml +81 -0
  70. src/vendor/php-http/curl-client/.php_cs +9 -0
  71. src/vendor/php-http/curl-client/CHANGELOG.md +211 -0
  72. src/vendor/{symfony/service-contracts → php-http/curl-client}/LICENSE +1 -1
  73. src/vendor/php-http/curl-client/README.md +42 -0
  74. src/vendor/php-http/curl-client/composer.json +57 -0
  75. src/vendor/php-http/curl-client/puli.json +242 -0
  76. src/vendor/php-http/curl-client/src/Client.php +398 -0
  77. src/vendor/php-http/curl-client/src/CurlPromise.php +111 -0
  78. src/vendor/php-http/curl-client/src/MultiRunner.php +131 -0
  79. src/vendor/php-http/curl-client/src/PromiseCore.php +252 -0
  80. src/vendor/php-http/curl-client/src/ResponseBuilder.php +24 -0
  81. src/vendor/php-http/discovery/CHANGELOG.md +4 -0
  82. src/vendor/php-http/discovery/src/Exception.php +3 -1
  83. src/vendor/psr/container/.gitignore +0 -3
  84. src/vendor/psr/container/LICENSE +0 -21
  85. src/vendor/psr/container/README.md +0 -13
  86. src/vendor/psr/container/composer.json +0 -22
  87. src/vendor/psr/container/src/ContainerExceptionInterface.php +0 -12
  88. src/vendor/psr/container/src/ContainerInterface.php +0 -36
  89. src/vendor/psr/container/src/NotFoundExceptionInterface.php +0 -10
  90. src/vendor/sentry/sentry/CHANGELOG.md +9 -0
  91. src/vendor/sentry/sentry/composer.json +1 -1
  92. src/vendor/sentry/sentry/src/Monolog/Handler.php +27 -1
  93. src/vendor/sentry/sentry/src/State/Scope.php +2 -6
  94. src/vendor/sentry/sentry/src/Tracing/GuzzleTracingMiddleware.php +1 -1
  95. src/vendor/sentry/sentry/src/Tracing/Span.php +21 -7
  96. src/vendor/sentry/sentry/src/Tracing/Transaction.php +1 -0
  97. src/vendor/symfony/http-client-contracts/.gitignore +0 -3
  98. src/vendor/symfony/http-client-contracts/CHANGELOG.md +0 -5
  99. src/vendor/symfony/http-client-contracts/ChunkInterface.php +0 -71
  100. src/vendor/symfony/http-client-contracts/Exception/ClientExceptionInterface.php +0 -21
  101. src/vendor/symfony/http-client-contracts/Exception/DecodingExceptionInterface.php +0 -21
  102. src/vendor/symfony/http-client-contracts/Exception/ExceptionInterface.php +0 -21
  103. src/vendor/symfony/http-client-contracts/Exception/HttpExceptionInterface.php +0 -24
  104. src/vendor/symfony/http-client-contracts/Exception/RedirectionExceptionInterface.php +0 -21
  105. src/vendor/symfony/http-client-contracts/Exception/ServerExceptionInterface.php +0 -21
  106. src/vendor/symfony/http-client-contracts/Exception/TimeoutExceptionInterface.php +0 -21
  107. src/vendor/symfony/http-client-contracts/Exception/TransportExceptionInterface.php +0 -21
  108. src/vendor/symfony/http-client-contracts/HttpClientInterface.php +0 -95
  109. src/vendor/symfony/http-client-contracts/LICENSE +0 -19
  110. src/vendor/symfony/http-client-contracts/README.md +0 -9
  111. src/vendor/symfony/http-client-contracts/ResponseInterface.php +0 -109
  112. src/vendor/symfony/http-client-contracts/ResponseStreamInterface.php +0 -26
  113. src/vendor/symfony/http-client-contracts/Test/Fixtures/web/index.php +0 -192
  114. src/vendor/symfony/http-client-contracts/Test/HttpClientTestCase.php +0 -1132
  115. src/vendor/symfony/http-client-contracts/Test/TestHttpServer.php +0 -46
  116. src/vendor/symfony/http-client-contracts/composer.json +0 -37
  117. src/vendor/symfony/http-client/AmpHttpClient.php +0 -176
  118. src/vendor/symfony/http-client/AsyncDecoratorTrait.php +0 -48
  119. src/vendor/symfony/http-client/CHANGELOG.md +0 -54
  120. src/vendor/symfony/http-client/CachingHttpClient.php +0 -152
  121. src/vendor/symfony/http-client/Chunk/DataChunk.php +0 -87
  122. src/vendor/symfony/http-client/Chunk/ErrorChunk.php +0 -140
  123. src/vendor/symfony/http-client/Chunk/FirstChunk.php +0 -28
  124. src/vendor/symfony/http-client/Chunk/InformationalChunk.php +0 -35
  125. src/vendor/symfony/http-client/Chunk/LastChunk.php +0 -28
  126. src/vendor/symfony/http-client/Chunk/ServerSentEvent.php +0 -79
  127. src/vendor/symfony/http-client/CurlHttpClient.php +0 -551
  128. src/vendor/symfony/http-client/DataCollector/HttpClientDataCollector.php +0 -170
  129. src/vendor/symfony/http-client/DecoratorTrait.php +0 -66
  130. src/vendor/symfony/http-client/DependencyInjection/HttpClientPass.php +0 -51
  131. src/vendor/symfony/http-client/EventSourceHttpClient.php +0 -159
  132. src/vendor/symfony/http-client/Exception/ClientException.php +0 -24
  133. src/vendor/symfony/http-client/Exception/EventSourceException.php +0 -21
  134. src/vendor/symfony/http-client/Exception/HttpExceptionTrait.php +0 -78
  135. src/vendor/symfony/http-client/Exception/InvalidArgumentException.php +0 -21
  136. src/vendor/symfony/http-client/Exception/JsonException.php +0 -23
  137. src/vendor/symfony/http-client/Exception/RedirectionException.php +0 -24
  138. src/vendor/symfony/http-client/Exception/ServerException.php +0 -24
  139. src/vendor/symfony/http-client/Exception/TimeoutException.php +0 -21
  140. src/vendor/symfony/http-client/Exception/TransportException.php +0 -21
  141. src/vendor/symfony/http-client/HttpClient.php +0 -78
  142. src/vendor/symfony/http-client/HttpClientTrait.php +0 -684
  143. src/vendor/symfony/http-client/HttpOptions.php +0 -331
  144. src/vendor/symfony/http-client/HttplugClient.php +0 -271
  145. src/vendor/symfony/http-client/Internal/AmpBody.php +0 -142
  146. src/vendor/symfony/http-client/Internal/AmpClientState.php +0 -217
  147. src/vendor/symfony/http-client/Internal/AmpListener.php +0 -183
  148. src/vendor/symfony/http-client/Internal/AmpResolver.php +0 -52
  149. src/vendor/symfony/http-client/Internal/Canary.php +0 -40
  150. src/vendor/symfony/http-client/Internal/ClientState.php +0 -26
  151. src/vendor/symfony/http-client/Internal/CurlClientState.php +0 -148
  152. src/vendor/symfony/http-client/Internal/DnsCache.php +0 -39
  153. src/vendor/symfony/http-client/Internal/HttplugWaitLoop.php +0 -141
  154. src/vendor/symfony/http-client/Internal/NativeClientState.php +0 -47
  155. src/vendor/symfony/http-client/Internal/PushedResponse.php +0 -41
  156. src/vendor/symfony/http-client/LICENSE +0 -19
  157. src/vendor/symfony/http-client/MockHttpClient.php +0 -124
  158. src/vendor/symfony/http-client/NativeHttpClient.php +0 -468
  159. src/vendor/symfony/http-client/NoPrivateNetworkHttpClient.php +0 -132
  160. src/vendor/symfony/http-client/Psr18Client.php +0 -239
  161. src/vendor/symfony/http-client/README.md +0 -27
  162. src/vendor/symfony/http-client/Response/AmpResponse.php +0 -461
  163. src/vendor/symfony/http-client/Response/AsyncContext.php +0 -189
  164. src/vendor/symfony/http-client/Response/AsyncResponse.php +0 -478
  165. src/vendor/symfony/http-client/Response/CommonResponseTrait.php +0 -185
  166. src/vendor/symfony/http-client/Response/CurlResponse.php +0 -474
  167. src/vendor/symfony/http-client/Response/HttplugPromise.php +0 -80
  168. src/vendor/symfony/http-client/Response/MockResponse.php +0 -339
  169. src/vendor/symfony/http-client/Response/NativeResponse.php +0 -376
  170. src/vendor/symfony/http-client/Response/ResponseStream.php +0 -54
  171. src/vendor/symfony/http-client/Response/StreamWrapper.php +0 -304
  172. src/vendor/symfony/http-client/Response/StreamableInterface.php +0 -35
  173. src/vendor/symfony/http-client/Response/TraceableResponse.php +0 -219
  174. src/vendor/symfony/http-client/Response/TransportResponseTrait.php +0 -312
  175. src/vendor/symfony/http-client/Retry/GenericRetryStrategy.php +0 -115
  176. src/vendor/symfony/http-client/Retry/RetryStrategyInterface.php +0 -36
  177. src/vendor/symfony/http-client/RetryableHttpClient.php +0 -169
  178. src/vendor/symfony/http-client/ScopingHttpClient.php +0 -131
  179. src/vendor/symfony/http-client/TraceableHttpClient.php +0 -120
  180. src/vendor/symfony/http-client/composer.json +0 -53
  181. src/vendor/symfony/service-contracts/.gitignore +0 -3
  182. src/vendor/symfony/service-contracts/Attribute/Required.php +0 -25
  183. src/vendor/symfony/service-contracts/Attribute/SubscribedService.php +0 -33
  184. src/vendor/symfony/service-contracts/CHANGELOG.md +0 -5
  185. src/vendor/symfony/service-contracts/README.md +0 -9
  186. src/vendor/symfony/service-contracts/ResetInterface.php +0 -30
  187. src/vendor/symfony/service-contracts/ServiceLocatorTrait.php +0 -128
  188. src/vendor/symfony/service-contracts/ServiceProviderInterface.php +0 -36
  189. src/vendor/symfony/service-contracts/ServiceSubscriberInterface.php +0 -53
  190. src/vendor/symfony/service-contracts/ServiceSubscriberTrait.php +0 -115
  191. src/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php +0 -95
  192. src/vendor/symfony/service-contracts/composer.json +0 -42
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.10
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.10');
30
  define('CNB_NAME', 'Call Now Button');
31
  define('CNB_BASENAME', plugin_basename(__FILE__));
32
  define('CNB_BASEFOLDER', plugin_basename(dirname(__FILE__)));
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.11
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.11');
30
  define('CNB_NAME', 'Call Now Button');
31
  define('CNB_BASENAME', plugin_basename(__FILE__));
32
  define('CNB_BASEFOLDER', plugin_basename(dirname(__FILE__)));
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.10
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -111,6 +111,11 @@ Yes, you can upgrade to Premium to enable tons of extra features. Checkout [call
111
 
112
 
113
  == Changelog ==
 
 
 
 
 
114
  = 1.1.10 =
115
  * Introduced geo targeting (Premium PRO)
116
  * New PRO feature: Reveal at scroll height (Premium PRO)
5
  Requires at least: 3.9
6
  Requires PHP: 5.4
7
  Tested up to: 6.0
8
+ Stable tag: 1.1.11
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.11 =
115
+ * Welcome page for new installations
116
+ * UI improvements
117
+ * Bugfixes
118
+
119
  = 1.1.10 =
120
  * Introduced geo targeting (Premium PRO)
121
  * New PRO feature: Reveal at scroll height (Premium PRO)
resources/images/button-scheduler.png ADDED
Binary file
resources/images/buttonbar.png ADDED
Binary file
resources/images/cnb-icons-actions.png ADDED
Binary file
resources/images/icon-256x256.png ADDED
Binary file
resources/images/multibutton.png ADDED
Binary file
resources/images/whatsapp-modal.png ADDED
Binary file
resources/js/action-type-to-icon-text.js CHANGED
@@ -25,9 +25,9 @@ function cnbActiontypeToIcontext(actionType) {
25
  }
26
  }
27
 
28
- function cnb_trigger_rerender(jEle) {
 
29
  jEle.trigger('change')
30
-
31
  }
32
  /**
33
  * Updates the iconText property (and rerenders the preview)
@@ -51,7 +51,8 @@ function cnb_change_icon_text(ele) {
51
  cnb_action_icon_type.val(jQuery(ele).data('icon-type'))
52
 
53
  // Force an update to the preview
54
- cnb_trigger_rerender(cnb_action_icon_text)
 
55
  } else {
56
  // Maybe it's the button-edit multibutton options
57
  // If so, get the parent and find if the 2 data attributes are set
@@ -63,7 +64,7 @@ function cnb_change_icon_text(ele) {
63
  const iconTypeEle = jQuery('#' + iconType)
64
  iconTypeEle.val(jQuery(ele).data('icon-type'))
65
  iconTextEle.val(val)
66
- iconTextEle.trigger('change')
67
  }
68
  }
69
  return false
25
  }
26
  }
27
 
28
+ function cnb_trigger_rerender(jEle, doNotExpand = false) {
29
+ window.cnbMultiDoNotExpand = doNotExpand
30
  jEle.trigger('change')
 
31
  }
32
  /**
33
  * Updates the iconText property (and rerenders the preview)
51
  cnb_action_icon_type.val(jQuery(ele).data('icon-type'))
52
 
53
  // Force an update to the preview
54
+ // Since the open icon changed, ensure it is CLOSED now (so you can see the change)
55
+ cnb_trigger_rerender(cnb_action_icon_text, true)
56
  } else {
57
  // Maybe it's the button-edit multibutton options
58
  // If so, get the parent and find if the 2 data attributes are set
64
  const iconTypeEle = jQuery('#' + iconType)
65
  iconTypeEle.val(jQuery(ele).data('icon-type'))
66
  iconTextEle.val(val)
67
+ cnb_trigger_rerender(iconTextEle)
68
  }
69
  }
70
  return false
resources/js/call-now-button.js CHANGED
@@ -64,13 +64,13 @@ function cnb_hide_on_show_always() {
64
  jQuery('.cnb_hide_on_show_always').hide();
65
 
66
  // Hide Domain Timezone notice
67
- jQuery('#cnb-notice-domain-timezone-unsupported').parent('.notice').hide();
68
  } else {
69
  // Show all items specific for Scheduler
70
  jQuery('.cnb_hide_on_show_always').show();
71
 
72
  // Show Domain Timezone notice (and move to the correct place)
73
- const domainTimezoneNotice = jQuery('#cnb-notice-domain-timezone-unsupported').parent('.notice');
74
  domainTimezoneNotice.show();
75
  const domainTimezoneNoticePlaceholder = jQuery('#domain-timezone-notice-placeholder');
76
  if (domainTimezoneNoticePlaceholder.length !== 0) {
64
  jQuery('.cnb_hide_on_show_always').hide();
65
 
66
  // Hide Domain Timezone notice
67
+ jQuery('.cnb-notice-domain-timezone-unsupported').parent('.notice').hide();
68
  } else {
69
  // Show all items specific for Scheduler
70
  jQuery('.cnb_hide_on_show_always').show();
71
 
72
  // Show Domain Timezone notice (and move to the correct place)
73
+ const domainTimezoneNotice = jQuery('.cnb-notice-domain-timezone-unsupported').parent('.notice');
74
  domainTimezoneNotice.show();
75
  const domainTimezoneNoticePlaceholder = jQuery('#domain-timezone-notice-placeholder');
76
  if (domainTimezoneNoticePlaceholder.length !== 0) {
resources/js/deactivation.js CHANGED
@@ -1,5 +1,3 @@
1
- const cnb_tally_deactivate_plugin_form_id = 'waQ6eb'
2
-
3
  function cnb_add_deactivation_init() {
4
  cnb_add_deactivation_popup_tally()
5
  }
@@ -8,9 +6,9 @@ function cnb_add_deactivation_init() {
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',
@@ -31,7 +29,11 @@ function cnb_add_deactivation_popup_tally() {
31
  }, 7000)
32
  },
33
  }
34
- Tally.openPopup(cnb_tally_deactivate_plugin_form_id, options)
 
 
 
 
35
  })
36
  }
37
 
 
 
1
  function cnb_add_deactivation_init() {
2
  cnb_add_deactivation_popup_tally()
3
  }
6
  * Called when the Call Now Button plugin's "Deactivate" link is clicked
7
  */
8
  function cnb_add_deactivation_popup_tally() {
9
+ const cnb_tally_deactivate_plugin_form_id = 'waQ6eb'
10
  const original_link = jQuery('#deactivate-call-now-button')
11
  original_link.on('click', (event) => {
 
12
 
13
  const options = {
14
  layout: 'modal',
29
  }, 7000)
30
  },
31
  }
32
+ // Check if Tally actually is loaded
33
+ if (Tally) {
34
+ event.preventDefault()
35
+ Tally.openPopup(cnb_tally_deactivate_plugin_form_id, options)
36
+ }
37
  })
38
  }
39
 
resources/js/premium-activation.js ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function cnb_email_activation_reenable_fields(form, showSomethingWentWrong = true) {
2
+ let errorMessage = ''
3
+ if (showSomethingWentWrong) {
4
+ errorMessage = '<h3 class="title">Something went wrong!</h3>' +
5
+ '<p>Something has gone wrong and we do not know why...</p>' +
6
+ '<p>As unlikely as it is, our service might be experiencing issues (check <a href="https://status.callnowbutton.com" target="_blank">our status page</a>).</p>' +
7
+ '<p>If you think you\'ve found a bug, please report it at our <a href="https://callnowbutton.com/support/" target="_blank">Help Center</a>.' +
8
+ '<p>Technical details:</p>'
9
+ }
10
+ const errorDetails = '<p style="color:red"><span class="cnb_email_activation_errors"></span></p>'
11
+
12
+ const submitButton = jQuery(form).find('[name="cnb_email_activation_submit"]')
13
+ jQuery(form).find('[name="cnb_email_activation_address"]').removeAttr("disabled")
14
+ submitButton.removeAttr("disabled")
15
+ submitButton.val("Activate Premium")
16
+ jQuery(form).find('.cnb_email_activation_message').html(errorMessage + errorDetails)
17
+ }
18
+
19
+ function cnb_email_activation_taking_too_long(form) {
20
+ const errorMessage = '<h3 class="title">Hmm, that\'s taking a while...</h3>' +
21
+ '<p>This call should not take this long. Please try again in a minute or so.</p>' +
22
+ '<p>As unlikely as it is, our service might be experiencing issues (check <a href="https://status.callnowbutton.com" target="_blank">our status page</a>).</p>' +
23
+ '<p>If you think you\'ve found a bug, please report it at our <a href="https://callnowbutton.com/support/" target="_blank">Help Center</a>.'
24
+ const errorDetails = '<p>Technical details:</p><p style="color:red"><span class="cnb_email_activation_errors"></span></p>'
25
+
26
+ const submitButton = jQuery(form).find('[name="cnb_email_activation_submit"]')
27
+ jQuery(form).find('[name="cnb_email_activation_address"]').removeAttr("disabled")
28
+ submitButton.removeAttr("disabled")
29
+ submitButton.val("Activate Premium")
30
+ jQuery(form).find('.cnb_email_activation_message').html(errorMessage + errorDetails)
31
+ }
32
+
33
+ /**
34
+ * This calls the admin-ajax action called 'cnb_email_activation' (function cnb_admin_cnb_email_activation)
35
+ */
36
+ function cnb_email_activation(form) {
37
+ const email_address = jQuery(form).find('[name="cnb_email_activation_address"]').val()
38
+
39
+ // Prep data
40
+ const data = {
41
+ 'action': 'cnb_email_activation',
42
+ 'admin_email': email_address
43
+ }
44
+
45
+ // Disable the Email and Button fields (reactivate in case of errors)
46
+ const submitButton = jQuery(form).find('[name="cnb_email_activation_submit"]')
47
+ jQuery(form).find('[name="cnb_email_activation_address"]').attr("disabled", "disabled")
48
+ submitButton.attr("disabled", "disabled")
49
+ submitButton.val("Check your e-mail")
50
+
51
+ // Clear the error fields
52
+ jQuery(form).find('.cnb_email_activation_message').empty()
53
+ jQuery(form).find('[name="cnb_email_activation_address"]').empty()
54
+
55
+ const statusTimeout = 5000
56
+ const fn_is_taking_too_long = () => cnb_email_activation_taking_too_long(form)
57
+ const takingTooLongTimer = setTimeout(fn_is_taking_too_long, statusTimeout)
58
+
59
+ // Send remove request
60
+ jQuery.post(ajaxurl, data)
61
+ .done((result) => {
62
+ if (result && result.email) {
63
+ clearTimeout(takingTooLongTimer)
64
+ jQuery(form).find('.cnb_email_activation_message').html('<span class="cnb_check_email_message">Check your inbox for an activation email sent to <strong><span class="cnb_email_activation_address"></span></strong>.</span>')
65
+ jQuery(form).find('span.cnb_email_activation_address').text(result.email)
66
+ }
67
+
68
+ if (result && result.errors) {
69
+ clearTimeout(takingTooLongTimer)
70
+ const keys = Object.keys(result.errors)
71
+
72
+ let showSomethingWentWrong = true
73
+ if (keys.length === 1 && (keys[0] === 'CNB_EMAIL_INVALID'|| keys[0] === 'CNB_EMAIL_EMPTY')) {
74
+ // Skip showing the big block with links, since we know exactly what's going on
75
+ showSomethingWentWrong = false
76
+ }
77
+ cnb_email_activation_reenable_fields(form, showSomethingWentWrong)
78
+
79
+ keys.forEach((key) => {
80
+ // Create Text Nodes to ensure escaping of the content
81
+ const codeMsg = document.createTextNode(key)
82
+ const errorMsg = document.createTextNode(result.errors[key])
83
+ const code = jQuery('<code>').append(codeMsg)
84
+ jQuery(form).find('.cnb_email_activation_errors').append('<br />', code, ': ', errorMsg)
85
+ })
86
+ }
87
+ })
88
+ .fail((result) => {
89
+ clearTimeout(takingTooLongTimer)
90
+ cnb_email_activation_reenable_fields(form)
91
+
92
+ // Create Text Nodes to ensure escaping of the content
93
+ const codeMsg = document.createTextNode(result.status + ' ' + result.statusText)
94
+ const errorMsg = document.createTextNode(result.responseText)
95
+ const code = jQuery('<code>').append(codeMsg)
96
+ jQuery(form).find('.cnb_email_activation_errors').append('<br />', code, ': ', errorMsg)
97
+ })
98
+ return false
99
+ }
100
+
101
+ function cnb_email_activation_init() {
102
+ jQuery('form.cnb_email_activation').on('submit', (event) => {
103
+ const form = jQuery(event.target)
104
+ return cnb_email_activation(form)
105
+ })
106
+ }
107
+
108
+ jQuery(() => {
109
+ cnb_email_activation_init()
110
+ })
resources/js/preview.js CHANGED
@@ -194,9 +194,15 @@ async function livePreview() {
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'))
@@ -204,10 +210,13 @@ async function livePreview() {
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)
213
  const button = jQuery('.cnb-single.call-now-button, .cnb-full.call-now-button, .cnb-multi.call-now-button').detach()
194
  // pass "false" to ensure we do NOT add the client's native observers
195
  const result = await CNB.render(cnbData, false)
196
 
197
+ const isWhatsappEditScreen = parsedData.action_id && parsedData.actions &&
198
+ parsedData.actions[parsedData.action_id] &&
199
+ parsedData.actions[parsedData.action_id].actionType === 'WHATSAPP'
200
+
201
+ // If there is a Whatsapp modal, trigger it
202
  // The "parsedData.action_id" check is to ensure it does not expand on a FULL or MULTI overview page
203
+ // We should also NOT expand if the edit screen for the current type is NOT a WhatsApp edit screen
204
+ // Basically, only expand on a WhatsApp edit screen
205
+ if (isWhatsappEditScreen) {
206
  const whatsappButton = jQuery('.call-now-button a[data-action-type="WHATSAPP"]')
207
  if (whatsappButton.length > 0) {
208
  whatsappButton[0].dispatchEvent(new window.CustomEvent('toggle'))
210
  }
211
 
212
  // If there is a Multibutton, expand it (test this AFTER the modal, so we only toggle if it isn't ALREADY expanded)
213
+ if (!window.cnbMultiDoNotExpand) {
214
+ const multiButton = jQuery('.cnb-multi.call-now-button:not(.cnb-expand) .cnb-floating-main')
215
+ if (multiButton.length > 0) {
216
+ multiButton[0].dispatchEvent(new window.CustomEvent('toggle'))
217
+ }
218
  }
219
+ window.cnbMultiDoNotExpand = undefined
220
 
221
  // Move the result into a new special div (if found)
222
  const button = jQuery('.cnb-single.call-now-button, .cnb-full.call-now-button, .cnb-multi.call-now-button').detach()
resources/js/settings.js CHANGED
@@ -1,105 +1,3 @@
1
- function cnb_email_activation_reenable_fields(showSomethingWentWrong = true) {
2
- let errorMessage = ''
3
- if (showSomethingWentWrong) {
4
- errorMessage = '<h3 class="title">Something went wrong!</h3>' +
5
- '<p>Something has gone wrong and we do not know why...</p>' +
6
- '<p>As unlikely as it is, our service might be experiencing issues (check <a href="https://status.callnowbutton.com">our status page</a>).</p>' +
7
- '<p>If you think you\'ve found a bug, please report it at our <a href="https://callnowbutton.com/support/" target="_blank">Help Center</a>.' +
8
- '<p>Technical details:</p>';
9
- }
10
- const errorDetails = '<p style="color:red"><span id="cnb_email_activation_details"></span></p>';
11
-
12
- const submitButton = jQuery('#cnb_email_activation_alternate')
13
- jQuery('#cnb_email_activation_alternate_address').removeAttr("disabled")
14
- submitButton.removeAttr("disabled")
15
- submitButton.val("Activate Premium")
16
- jQuery('#cnb_email_activation').html(errorMessage + errorDetails);
17
- }
18
-
19
- function cnb_email_activation_taking_too_long() {
20
- const errorMessage = '<h3 class="title">Hmm, that\'s taking a while...</h3>' +
21
- '<p>This call should not take this long. Please try again in a minute or so.</p>' +
22
- '<p>As unlikely as it is, our service might be experiencing issues (check <a href="https://status.callnowbutton.com">our status page</a>).</p>' +
23
- '<p>If you think you\'ve found a bug, please report it at our <a href="https://callnowbutton.com/support/" target="_blank">Help Center</a>.';
24
- const errorDetails = '<p>Technical details:</p><p style="color:red"><span id="cnb_email_activation_details"></span></p>';
25
-
26
- const submitButton = jQuery('#cnb_email_activation_alternate')
27
- jQuery('#cnb_email_activation_alternate_address').removeAttr("disabled")
28
- submitButton.removeAttr("disabled")
29
- submitButton.val("Activate Premium")
30
- jQuery('#cnb_email_activation').html(errorMessage + errorDetails);
31
- }
32
-
33
- /**
34
- * This calls the admin-ajax action called 'cnb_email_activation' (function cnb_admin_cnb_email_activation)
35
- */
36
- function cnb_email_activation(admin_email) {
37
- // Prep data
38
- const data = {
39
- 'action': 'cnb_email_activation',
40
- 'admin_email': admin_email
41
- };
42
-
43
- // Disable the Email and Button fields (reactivate in case of errors)
44
- const submitButton = jQuery('#cnb_email_activation_alternate')
45
- jQuery('#cnb_email_activation_alternate_address').attr("disabled", "disabled")
46
- submitButton.attr("disabled", "disabled")
47
- submitButton.val("Check your e-mail")
48
-
49
- // Clear the error fields
50
- jQuery('#cnb_email_activation').empty()
51
- jQuery('#cnb_email_activation_email').empty()
52
-
53
- const statusTimeout = 5000
54
- const takingTooLongTimer = setTimeout(cnb_email_activation_taking_too_long, statusTimeout)
55
-
56
- // Send remove request
57
- jQuery.post(ajaxurl, data)
58
- .done((result) => {
59
- if (result && result.email) {
60
- clearTimeout(takingTooLongTimer)
61
- jQuery('#cnb_email_activation').html('<span class="cnb_check_email_message">Check your inbox for an activation email sent to <strong><span id="cnb_email_activation_email"></span></strong>.</span>')
62
- jQuery('#cnb_email_activation_email').text(result.email)
63
- }
64
-
65
- if (result && result.errors) {
66
- clearTimeout(takingTooLongTimer)
67
- const keys = Object.keys(result.errors)
68
-
69
- let showSomethingWentWrong = true
70
- if (keys.length === 1 && (keys[0] === 'CNB_EMAIL_INVALID'|| keys[0] === 'CNB_EMAIL_EMPTY')) {
71
- // Skip showing the big block with links, since we know exactly what's going on
72
- showSomethingWentWrong = false
73
- }
74
- cnb_email_activation_reenable_fields(showSomethingWentWrong)
75
-
76
- keys.forEach((key) => {
77
- // Create Text Nodes to ensure escaping of the content
78
- const codeMsg = document.createTextNode(key)
79
- const errorMsg = document.createTextNode(result.errors[key])
80
- const code = jQuery('<code>').append(codeMsg)
81
- jQuery('#cnb_email_activation_details').append('<br />', code, ': ', errorMsg);
82
- })
83
- }
84
- })
85
- .fail((result) => {
86
- clearTimeout(takingTooLongTimer)
87
- cnb_email_activation_reenable_fields()
88
-
89
- // Create Text Nodes to ensure escaping of the content
90
- const codeMsg = document.createTextNode(result.status + ' ' + result.statusText)
91
- const errorMsg = document.createTextNode(result.responseText)
92
- const code = jQuery('<code>').append(codeMsg)
93
- jQuery('#cnb_email_activation_details').append('<br />', code, ': ', errorMsg);
94
- });
95
- return false;
96
- }
97
-
98
- function cnb_email_activation_submit() {
99
- const alternate_admin_email = jQuery('#cnb_email_activation_alternate_address').val()
100
- return cnb_email_activation(alternate_admin_email);
101
- }
102
-
103
  // Note: IDE marks this as unused, but it is used by settings-edit.php ("Delete API key")
104
  function cnb_delete_apikey() {
105
  const apiKeyField = jQuery(".call-now-button-plugin #cnb_api_key")
@@ -136,10 +34,13 @@ function cnb_ask_for_feedback_disable_cloud() {
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
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  // Note: IDE marks this as unused, but it is used by settings-edit.php ("Delete API key")
2
  function cnb_delete_apikey() {
3
  const apiKeyField = jQuery(".call-now-button-plugin #cnb_api_key")
34
  userId: jQuery('#cnb_user_id').text()
35
  }
36
  }
37
+ // Check if Tally actually is loaded
38
+ if (Tally) {
39
+ if (!isChecked) {
40
+ Tally.openPopup(cnb_tally_deactivate_premium_form_id, options)
41
+ } else {
42
+ Tally.closePopup(cnb_tally_deactivate_premium_form_id)
43
+ }
44
  }
45
  })
46
  }
resources/style/call-now-button.css CHANGED
@@ -1226,3 +1226,127 @@ span.cnb-pro-badge {
1226
  color: rgb(0 153 0);
1227
  background: rgba(0, 153, 0, 0.125);
1228
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1226
  color: rgb(0 153 0);
1227
  background: rgba(0, 153, 0, 0.125);
1228
  }
1229
+ .cnb-welcome-page {
1230
+ padding-right: 20px;
1231
+ padding-top: 70px;
1232
+ }
1233
+ @media screen and (max-width: 782px) {
1234
+ .cnb-welcome-page {
1235
+ padding-right: 10px;
1236
+ }
1237
+ }
1238
+ .cnb-welcome-blocks {
1239
+ max-width:860px;
1240
+ box-sizing: border-box;
1241
+ padding:20px;
1242
+ margin: 20px auto;
1243
+ border:1px solid #ccc;
1244
+ position: relative;
1245
+ text-align: center;
1246
+ background-color: #fff;
1247
+ }
1248
+ .cnb-welcome-blocks .cnb-logo {
1249
+ margin-top:calc(-35px - 64px);
1250
+ padding:15px 15px 13px;
1251
+ border-radius: 50%;
1252
+ border:1px solid #ccc;
1253
+ background: #fff;
1254
+ }
1255
+ .cnb-welcome-blocks h3 {
1256
+ font-weight: normal;
1257
+ color:#666;
1258
+ line-height: 1.4;
1259
+ }
1260
+ .cnb-welcome-blocks .cnb-divider {
1261
+ height: 0;
1262
+ border-top:1px solid #ccc;
1263
+ margin: 30px 0;
1264
+ }
1265
+ .cnb-welcome-blocks .cnb-block .cnb-divider {
1266
+ border-color:#ddd;
1267
+ }
1268
+ .cnb-welcome-blocks .cnb-block {
1269
+ max-width: 700px;
1270
+ margin: 20px auto;
1271
+ text-align: center;
1272
+ }
1273
+ .cnb-welcome-blocks .cnb-block img {
1274
+ max-width: 700px;
1275
+ width: 100%;
1276
+ height: auto;
1277
+ }
1278
+ .cnb-welcome-blocks .cnb-block img.cnb-width-80 {
1279
+ max-width: calc(700px * 0.8);
1280
+ width: 80%;
1281
+ }
1282
+ .cnb-welcome-blocks .cnb-block img.cnb-width-70 {
1283
+ max-width: calc(700px * 0.7);
1284
+ width: 70%;
1285
+ }
1286
+ .cnb-welcome-blocks .cnb-extra-space {
1287
+ margin-top:30px;
1288
+ margin-bottom: 30px;
1289
+ }
1290
+ .cnb-welcome-blocks .cnb-features-list {
1291
+ max-width: 80%;
1292
+ display: flex;
1293
+ text-align: left;
1294
+ }
1295
+ .cnb-welcome-blocks .cnb-features-list .cnb-column {
1296
+ padding: 0 15px;
1297
+ box-sizing: border-box;
1298
+ width: 50%;
1299
+ }
1300
+ @media screen and (max-width: 750px) {
1301
+ .cnb-welcome-blocks .cnb-features-list .cnb-column p {
1302
+ display: none;
1303
+ }
1304
+ .cnb-welcome-blocks .cnb-features-list {
1305
+ max-width: 100%;
1306
+ }
1307
+ }
1308
+ @media screen and (max-width: 550px) {
1309
+ .cnb-welcome-blocks .cnb-features-list {
1310
+ flex-direction: column;
1311
+ }
1312
+ .cnb-welcome-blocks .cnb-features-list .cnb-column {
1313
+ width: 100%;
1314
+ text-align: center;
1315
+ }
1316
+ }
1317
+ .cnb-welcome-blocks .cnb-signup-box {
1318
+ max-width:550px;
1319
+ }
1320
+ .cnb-welcome-blocks .cnb-signup-box .cnb_activation_input_field {
1321
+ padding:8.5px 10px;
1322
+ }
1323
+
1324
+ .cnb-welcome-blocks .cnb-features-list p {
1325
+ font-size:14px;
1326
+ }
1327
+ .cnb-welcome-blocks .cnb_activation_input_field {
1328
+ width: calc(100% - 195px);
1329
+ }
1330
+ @media screen and (max-width: 450px) {
1331
+ .cnb-welcome-blocks .cnb_activation_input_field {
1332
+ width: 100%;
1333
+ }
1334
+ }
1335
+ .cnb-welcome-blocks .button.button-primary {
1336
+ background: #009900;
1337
+ border-color: #009900;
1338
+ font-size:16px;
1339
+ padding:5px 30px;
1340
+ }
1341
+ .cnb-welcome-blocks .button.button-primary:hover,
1342
+ .cnb-welcome-blocks .button.button-primary:active {
1343
+ background: #007700;
1344
+ border-color: #007700;
1345
+ }
1346
+ .cnb-welcome-blocks .button.button-primary:focus {
1347
+ box-shadow: 0 0 0 1px #fff, 0 0 0 3px #009900;
1348
+ }
1349
+ .cnb_disabled_feature,
1350
+ .cnb_disabled_feature th {
1351
+ color:rgba(29,35,39,0.5);
1352
+ }
src/CallNowButton.php CHANGED
@@ -15,10 +15,12 @@ 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;
@@ -51,11 +53,9 @@ class CallNowButton {
51
  $menu_page_title = 'Call Now Button<span class="awaiting-mod" id="cnb-nav-counter" style="display: none">' . $counter . '</span>';
52
  $menu_page_position = $cnb_cloud_hosting ? 30 : 66;
53
 
54
- $legacyController = new CnbLegacyController();
55
- $has_welcome_banner = $legacyController->show_welcome_banner() && ! $cnb_cloud_hosting;
56
- if ( $has_welcome_banner ) {
57
- $counter ++;
58
- }
59
 
60
  // Detect errors (specific, - Premium enabled, but API key is not present yet)
61
  if ( $cnb_cloud_hosting && ! array_key_exists( 'api_key', $cnb_options ) ) {
@@ -132,6 +132,28 @@ class CallNowButton {
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();
@@ -300,6 +322,12 @@ class CallNowButton {
300
  array( CNB_SLUG . '-call-now-button' ),
301
  CNB_VERSION,
302
  true );
 
 
 
 
 
 
303
  wp_register_script(
304
  CNB_SLUG . '-action-edit-scheduler',
305
  plugins_url( '../resources/js/action-edit-scheduler.js', __FILE__ ),
@@ -395,12 +423,18 @@ class CallNowButton {
395
 
396
  public function register_global_actions() {
397
  add_action( 'admin_menu', array( $this, 'register_admin_pages' ) );
 
 
 
398
  add_filter( 'plugin_row_meta', array( $this, 'plugin_meta' ), 10, 2 );
399
  add_filter( 'plugin_action_links_' . CNB_BASENAME, array( $this, 'plugin_add_action_link' ) );
400
 
401
  add_action( 'admin_init', array( $this, 'options_init' ) );
402
  add_action( 'admin_init', array( $this, 'register_styles_and_scripts' ) );
403
 
 
 
 
404
  $deactivation = new Deactivation();
405
  add_action( 'admin_init', array($deactivation, 'register_deactivation_popup' ) );
406
 
15
  use cnb\admin\CnbAdminAjax;
16
  use cnb\admin\condition\CnbConditionController;
17
  use cnb\admin\condition\CnbConditionRouter;
18
+ use cnb\admin\deactivation\Activation;
19
  use cnb\admin\deactivation\Deactivation;
20
  use cnb\admin\domain\CnbDomainController;
21
  use cnb\admin\domain\CnbDomainRouter;
22
+ use cnb\admin\gettingstarted\GettingStartedController;
23
+ use cnb\admin\gettingstarted\GettingStartedRouter;
24
  use cnb\admin\legacy\CnbLegacyEdit;
25
  use cnb\admin\legacy\CnbLegacyUpgrade;
26
  use cnb\admin\profile\CnbProfileController;
53
  $menu_page_title = 'Call Now Button<span class="awaiting-mod" id="cnb-nav-counter" style="display: none">' . $counter . '</span>';
54
  $menu_page_position = $cnb_cloud_hosting ? 30 : 66;
55
 
56
+ $header_notices = new CnbHeaderNotices();
57
+ $has_changelog = $header_notices->upgrade_notice();
58
+ if ($has_changelog) $counter++;
 
 
59
 
60
  // Detect errors (specific, - Premium enabled, but API key is not present yet)
61
  if ( $cnb_cloud_hosting && ! array_key_exists( 'api_key', $cnb_options ) ) {
132
  add_submenu_page( CNB_SLUG, $plugin_title, 'Settings', 'manage_options', CNB_SLUG . '-settings', array( $settings_router, 'render' ) );
133
  }
134
 
135
+ public function register_welcome_page() {
136
+ $utils = new CnbUtils();
137
+ $controller = new GettingStartedController();
138
+ $menu_slug = $controller->get_slug();
139
+
140
+ if ( $utils->get_query_val( 'page' ) === $menu_slug ) {
141
+ $getting_started_router = new GettingStartedRouter();
142
+ add_dashboard_page(
143
+ esc_html__( 'Welcome to Call Now Button' ),
144
+ esc_html__( 'Call Now Button' ),
145
+ 'manage_options',
146
+ $menu_slug,
147
+ array( $getting_started_router, 'render' )
148
+ );
149
+ }
150
+ }
151
+
152
+ public function hide_welcome_page() {
153
+ $controller = new GettingStartedController();
154
+ remove_submenu_page('index.php', $controller->get_slug());
155
+ }
156
+
157
  public function plugin_meta( $links, $file ) {
158
  $cnb_options = get_option( 'cnb' );
159
  $cnb_utils = new CnbUtils();
322
  array( CNB_SLUG . '-call-now-button' ),
323
  CNB_VERSION,
324
  true );
325
+ wp_register_script(
326
+ CNB_SLUG . '-premium-activation',
327
+ plugins_url( '../resources/js/premium-activation.js', __FILE__ ),
328
+ array( CNB_SLUG . '-call-now-button' ),
329
+ CNB_VERSION,
330
+ true );
331
  wp_register_script(
332
  CNB_SLUG . '-action-edit-scheduler',
333
  plugins_url( '../resources/js/action-edit-scheduler.js', __FILE__ ),
423
 
424
  public function register_global_actions() {
425
  add_action( 'admin_menu', array( $this, 'register_admin_pages' ) );
426
+ add_action( 'admin_menu', array( $this, 'register_welcome_page' ) );
427
+ add_action( 'admin_head', array( $this, 'hide_welcome_page' ) );
428
+
429
  add_filter( 'plugin_row_meta', array( $this, 'plugin_meta' ), 10, 2 );
430
  add_filter( 'plugin_action_links_' . CNB_BASENAME, array( $this, 'plugin_add_action_link' ) );
431
 
432
  add_action( 'admin_init', array( $this, 'options_init' ) );
433
  add_action( 'admin_init', array( $this, 'register_styles_and_scripts' ) );
434
 
435
+ $activation = new Activation();
436
+ add_action( 'admin_init', array($activation, 'redirect_to_welcome_page' ) );
437
+
438
  $deactivation = new Deactivation();
439
  add_action( 'admin_init', array($deactivation, 'register_deactivation_popup' ) );
440
 
src/admin/CnbAdminAjax.php CHANGED
@@ -134,30 +134,43 @@ class CnbAdminAjax {
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';
139
- } );
140
- $eur_yearly_plan = array_pop( $eur_yearly_plan );
141
- $eur_yearly_per_month = round( $eur_yearly_plan->price / 12.0, 2 );
142
-
143
- $usd_yearly_plan = array_filter( $plans, function ( $plan ) {
144
- return $plan->nickname === 'powered-by-usd-yearly';
145
- } );
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();
134
  $domain_controller = new CnbDomainController();
135
 
136
  $plans = CnbAppRemotePayment::cnb_remote_get_plans();
137
+
138
+ // Hardcoded fallback values in case the API call fails
139
+ $eur_yearly_per_month = 4.16;
140
+ $eur_discount = 17;
141
+ $usd_yearly_per_month = 4.99;
142
+ $usd_discount = 17;
143
+
144
+ if (is_array($plans)) {
145
+ $eur_yearly_plan = array_filter( $plans, function ( $plan ) {
146
+ return $plan->nickname === 'powered-by-eur-yearly';
147
+ } );
148
+ $eur_yearly_plan = array_pop( $eur_yearly_plan );
149
+ $eur_yearly_per_month = round( $eur_yearly_plan->price / 12.0, 2 );
150
+
151
+ $usd_yearly_plan = array_filter( $plans, function ( $plan ) {
152
+ return $plan->nickname === 'powered-by-usd-yearly';
153
+ } );
154
+ $usd_yearly_plan = array_pop( $usd_yearly_plan );
155
+ $usd_yearly_per_month = round( $usd_yearly_plan->price / 12.0, 2 );
156
+
157
+ $eur_monthly_plan = array_filter( $plans, function ( $plan ) {
158
+ return $plan->nickname === 'powered-by-eur-monthly';
159
+ } );
160
+ $usd_monthly_plan = array_filter( $plans, function ( $plan ) {
161
+ return $plan->nickname === 'powered-by-usd-monthly';
162
+ } );
163
+
164
+ // Calculate discounts
165
+ $eur_discount = $domain_controller->get_discount_percentage( $eur_yearly_plan, array_pop( $eur_monthly_plan ) );
166
+ $usd_discount = $domain_controller->get_discount_percentage( $usd_yearly_plan, array_pop( $usd_monthly_plan ) );
167
+ }
168
 
169
  wp_send_json( array(
170
  'eur_per_month' => $eur_yearly_per_month,
171
+ 'eur_discount' => $eur_discount,
172
  'usd_per_month' => $usd_yearly_per_month,
173
+ 'usd_discount' => $usd_discount,
174
  ) );
175
  do_action( 'cnb_finish' );
176
  wp_die();
src/admin/api/CnbDeleteResult.php CHANGED
@@ -33,14 +33,15 @@ class CnbDeleteResult {
33
  *
34
  * @param $object stdClass|array|WP_Error|null
35
  *
36
- * @return CnbDeleteResult|WP_Error
37
  */
38
  public static function fromObject( $object ) {
 
39
  if ( is_wp_error( $object ) ) {
40
- return $object;
 
41
  }
42
 
43
- $result = new CnbDeleteResult();
44
  // phpcs:ignore PHPCompatibility.FunctionUse
45
  $result->success = boolval( CnbUtils::getPropertyOrNull( $object, 'success' ) );
46
  $result->id = CnbUtils::getPropertyOrNull( $object, 'id' );
33
  *
34
  * @param $object stdClass|array|WP_Error|null
35
  *
36
+ * @return CnbDeleteResult
37
  */
38
  public static function fromObject( $object ) {
39
+ $result = new CnbDeleteResult();
40
  if ( is_wp_error( $object ) ) {
41
+ $result->object = $object;
42
+ return $result;
43
  }
44
 
 
45
  // phpcs:ignore PHPCompatibility.FunctionUse
46
  $result->success = boolval( CnbUtils::getPropertyOrNull( $object, 'success' ) );
47
  $result->id = CnbUtils::getPropertyOrNull( $object, 'id' );
src/admin/button/CnbButtonView.php CHANGED
@@ -87,8 +87,10 @@ class CnbButtonView {
87
  // Set filter
88
  $this->set_button_filter( $cnb_cloud_domain, $wp_list_table );
89
 
90
- // If users come to this page before activating, we need the -settings JS for the activation notice
91
  wp_enqueue_script( CNB_SLUG . '-settings' );
 
 
92
  add_action( 'cnb_header_name', array( $this, 'header' ) );
93
 
94
  $data = $wp_list_table->prepare_items();
87
  // Set filter
88
  $this->set_button_filter( $cnb_cloud_domain, $wp_list_table );
89
 
90
+ // If users come to this page before activating, we need the -settings/-premium-activation JS for the activation notice
91
  wp_enqueue_script( CNB_SLUG . '-settings' );
92
+ wp_enqueue_script( CNB_SLUG . '-premium-activation' );
93
+
94
  add_action( 'cnb_header_name', array( $this, 'header' ) );
95
 
96
  $data = $wp_list_table->prepare_items();
src/admin/deactivation/Activation.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace cnb\admin\deactivation;
4
 
 
5
  use cnb\CallNowButton;
6
 
7
  /**
@@ -9,6 +10,17 @@ use cnb\CallNowButton;
9
  */
10
  class Activation {
11
 
 
 
 
 
 
 
 
 
 
 
 
12
  /**
13
  * This is called /during/ the activation process.
14
  *
@@ -18,12 +30,52 @@ class Activation {
18
  * @return void
19
  */
20
  public static function onActivation() {
21
- // This has to called manually, since Activation is before the `admin_init` stage!
22
- $cnb = new CallNowButton();
23
- $cnb->options_init();
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  $options = get_option('cnb');
26
  $options['activation_time'] = time();
27
  update_option( 'cnb', $options );
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
2
 
3
  namespace cnb\admin\deactivation;
4
 
5
+ use cnb\admin\gettingstarted\GettingStartedController;
6
  use cnb\CallNowButton;
7
 
8
  /**
10
  */
11
  class Activation {
12
 
13
+ /**
14
+ * @var CallNowButton
15
+ */
16
+ private $cnb;
17
+
18
+ private $activation_transient = 'call-now-button_activation_redirect';
19
+
20
+ public function __construct() {
21
+ $this->cnb = new CallNowButton();
22
+ }
23
+
24
  /**
25
  * This is called /during/ the activation process.
26
  *
30
  * @return void
31
  */
32
  public static function onActivation() {
33
+ $activation = new Activation();
 
 
34
 
35
+ $activation->init_activation();
36
+ $activation->capture_activation_time();
37
+ $activation->ensure_redirect();
38
+ }
39
+
40
+ /**
41
+ * This has to called manually, since Activation is before the `admin_init` stage!
42
+ *
43
+ * @return void
44
+ */
45
+ private function init_activation() {
46
+ $this->cnb->options_init();
47
+
48
+ }
49
+ private function capture_activation_time() {
50
  $options = get_option('cnb');
51
  $options['activation_time'] = time();
52
  update_option( 'cnb', $options );
53
  }
54
+
55
+ private function ensure_redirect() {
56
+ set_transient($this->activation_transient, true);
57
+ }
58
+
59
+ public function redirect_to_welcome_page() {
60
+ $should_redirect = get_transient($this->activation_transient);
61
+ if (!$should_redirect) {
62
+ return;
63
+ }
64
+ delete_transient($this->activation_transient);
65
+
66
+ $controller = new GettingStartedController();
67
+ $menu_slug = $controller->get_slug();
68
+
69
+ $url = admin_url( 'index.php' );
70
+ $redirect_link =
71
+ add_query_arg(
72
+ array(
73
+ 'page' => $menu_slug,
74
+ ),
75
+ $url );
76
+
77
+ $redirect_url = esc_url_raw( $redirect_link );
78
+ wp_safe_redirect( $redirect_url );
79
+ exit;
80
+ }
81
  }
src/admin/domain/CnbDomainViewEdit.php CHANGED
@@ -80,7 +80,7 @@ class CnbDomainViewEdit {
80
  <?php
81
  if ( $domain->type !== 'PRO' && ! empty( $domain->id ) ) {
82
  echo '<a href="' . esc_url( $upgrade_link ) . '">Upgrade!</a>
83
- <p class="description">The FREE plan offers all features but adds delicate branding to your buttons.</p>';
84
  }
85
  ?>
86
  </td>
80
  <?php
81
  if ( $domain->type !== 'PRO' && ! empty( $domain->id ) ) {
82
  echo '<a href="' . esc_url( $upgrade_link ) . '">Upgrade!</a>
83
+ <p class="description">The FREE plan offers 99% of all features but adds delicate branding to your buttons.</p>';
84
  }
85
  ?>
86
  </td>
src/admin/domain/partials/CnbDomainViewUpgradeOverview.php CHANGED
@@ -38,7 +38,7 @@ class CnbDomainViewUpgradeOverview {
38
  * @return void
39
  */
40
  function render( $domain ) {
41
- if ( $domain->type !== 'FREE' ) { ?><p>Your domain is currently on the Premium
42
  <code><?php echo esc_html( $domain->type ) ?></code> plan.</p>
43
  <?php } ?>
44
 
@@ -147,7 +147,7 @@ class CnbDomainViewUpgradeOverview {
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>
150
- <div class="benefit">🧰 All features from Premium</div>
151
  <div class="benefit">✨ "Powered by" notice removed</div>
152
  <div class="benefit">🌍 Include and exclude countries</div>
153
  <div class="benefit">↕️ Set scroll height for buttons to appear</div>
@@ -174,7 +174,7 @@ class CnbDomainViewUpgradeOverview {
174
  ?>
175
  <div class="pricebox">
176
  <h3 class="monthly"><span class="cnb-premium-label">PRO </span>Monthly</h3>
177
- <div class="benefit">🧰 All features from Premium</div>
178
  <div class="benefit">✨ "Powered by" notice removed</div>
179
  <div class="benefit">🌍 Include and exclude countries</div>
180
  <div class="benefit">↕️ Set scroll height for buttons to appear</div>
@@ -208,7 +208,7 @@ class CnbDomainViewUpgradeOverview {
208
  ?>
209
  <div class="pricebox">
210
  <h3 class="yearly"><span class="cnb-premium-label">PRO </span>Yearly <span class="cnb-green">Save <?php echo esc_html( $annual_discount ); ?>%!</span></h3>
211
- <div class="benefit">🧰 All features from Premium</div>
212
  <div class="benefit">✨ "Powered by" notice removed</div>
213
  <div class="benefit">🌍 Include and exclude countries</div>
214
  <div class="benefit">↕️ Set scroll height for buttons to appear</div>
@@ -234,7 +234,7 @@ class CnbDomainViewUpgradeOverview {
234
  ?>
235
  <div class="pricebox">
236
  <h3 class="monthly"><span class="cnb-premium-label">PRO </span>Monthly</h3>
237
- <div class="benefit">🧰 All features from Premium</div>
238
  <div class="benefit">✨ "Powered by" notice removed</div>
239
  <div class="benefit">🌍 Include and exclude countries</div>
240
  <div class="benefit">↕️ Set scroll height for buttons to appear</div>
38
  * @return void
39
  */
40
  function render( $domain ) {
41
+ if ( $domain->type !== 'FREE' ) { ?><p>Your domain is currently on the
42
  <code><?php echo esc_html( $domain->type ) ?></code> plan.</p>
43
  <?php } ?>
44
 
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>
150
+ <div class="benefit">🧰 All features from Cloud</div>
151
  <div class="benefit">✨ "Powered by" notice removed</div>
152
  <div class="benefit">🌍 Include and exclude countries</div>
153
  <div class="benefit">↕️ Set scroll height for buttons to appear</div>
174
  ?>
175
  <div class="pricebox">
176
  <h3 class="monthly"><span class="cnb-premium-label">PRO </span>Monthly</h3>
177
+ <div class="benefit">🧰 All features from Cloud</div>
178
  <div class="benefit">✨ "Powered by" notice removed</div>
179
  <div class="benefit">🌍 Include and exclude countries</div>
180
  <div class="benefit">↕️ Set scroll height for buttons to appear</div>
208
  ?>
209
  <div class="pricebox">
210
  <h3 class="yearly"><span class="cnb-premium-label">PRO </span>Yearly <span class="cnb-green">Save <?php echo esc_html( $annual_discount ); ?>%!</span></h3>
211
+ <div class="benefit">🧰 All features from Cloud</div>
212
  <div class="benefit">✨ "Powered by" notice removed</div>
213
  <div class="benefit">🌍 Include and exclude countries</div>
214
  <div class="benefit">↕️ Set scroll height for buttons to appear</div>
234
  ?>
235
  <div class="pricebox">
236
  <h3 class="monthly"><span class="cnb-premium-label">PRO </span>Monthly</h3>
237
+ <div class="benefit">🧰 All features from Cloud</div>
238
  <div class="benefit">✨ "Powered by" notice removed</div>
239
  <div class="benefit">🌍 Include and exclude countries</div>
240
  <div class="benefit">↕️ Set scroll height for buttons to appear</div>
src/admin/getting-started/class-getting-started-controller.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\admin\gettingstarted;
4
+
5
+ // don't load directly
6
+ defined( 'ABSPATH' ) || die( '-1' );
7
+
8
+ class GettingStartedController {
9
+
10
+ public function get_slug() {
11
+ return CNB_SLUG . '-getting-started';
12
+ }
13
+ }
src/admin/getting-started/class-getting-started-router.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\admin\gettingstarted;
4
+
5
+ // don't load directly
6
+ defined( 'ABSPATH' ) || die( '-1' );
7
+
8
+ class GettingStartedRouter {
9
+
10
+ public function render() {
11
+ $view = new GettingStartedView();
12
+ $view->render();
13
+ }
14
+ }
src/admin/getting-started/class-getting-started-view.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace cnb\admin\gettingstarted;
4
+
5
+ // don't load directly
6
+ defined( 'ABSPATH' ) || die( '-1' );
7
+
8
+ use cnb\CnbHeaderNotices;
9
+
10
+ class GettingStartedView {
11
+ public function render() {
12
+
13
+ wp_enqueue_style( CNB_SLUG . '-styling' );
14
+ wp_enqueue_script( CNB_SLUG . '-premium-activation' );
15
+
16
+ // Create link to the regular legacy page
17
+ $url = admin_url( 'admin.php' );
18
+ $link =
19
+ add_query_arg(
20
+ array(
21
+ 'page' => 'call-now-button'
22
+ ),
23
+ $url );
24
+ ?>
25
+ <div class="cnb-welcome-page">
26
+ <div class="cnb-welcome-blocks cnb-extra-top">
27
+
28
+ <img class="cnb-logo" src="<?php echo esc_url(WP_PLUGIN_URL . '/' . CNB_BASEFOLDER . '/resources/images/icon-256x256.png');?>" width="128" height="128" alt="Call Now Button icon" />
29
+ <h1>Welcome to Call Now Button</h1>
30
+ <h3>Thank you for choosing Call Now Button - The web's most popular click-to-call button</h3>
31
+ <div class="cnb-divider"></div>
32
+ <h2>Create a (free) account to enable additional features:</h2>
33
+ <div class="cnb-block cnb-features-list">
34
+ <div class="cnb-column cnb-col-1">
35
+ <h3>🎁 Additional actions</h3>
36
+ <p>SMS/Text, Email, Maps, URLs, Scroll to point, WhatsApp, Messenger, Telegram, Signal</p>
37
+ <h3>🆕 Lots of buttons</h3>
38
+ <p>Multiple buttons for your website, even on a single page</p>
39
+ <h3>🗂️ Multi action buttons</h3>
40
+ <p>Multibutton (expandable) and Buttonbar (full width)</p>
41
+ <h3>💬 WhatsApp chat modal</h3>
42
+ <p>A WhatsApp chat panel to slide into the screen</p>
43
+ </div>
44
+
45
+ <div class="cnb-column cnb-col-2">
46
+ <h3>🖥️ All devices</h3>
47
+ <p>Desktop/laptop and mobile support</p>
48
+ <h3>🎯 Advanced page targeting</h3>
49
+ <p>Create smart rules for your buttons to appear</p>
50
+ <h3>🕘 Scheduler</h3>
51
+ <p>Create a weekly schedule for your buttons</p>
52
+ <h3>👋 Animations</h3>
53
+ <p>Add extra attention grabbing animations</p>
54
+ <h3>🎨 Icon picker</h3>
55
+ <p>Select the right icon for your button</p>
56
+ </div>
57
+ </div>
58
+ <div class="cnb-block cnb-signup-box">
59
+ <h2>Sign up now to get all this and more</h2>
60
+ <?php echo CnbHeaderNotices::cnb_settings_email_activation_input(); // phpcs:ignore WordPress.Security ?>
61
+ </div>
62
+ <div class="cnb-divider"></div>
63
+ <p><i>Only need a Call button? <a href="<?php echo esc_url( $link ) ?>">Continue without an account</a>.</i></p>
64
+ </div>
65
+ <div class="cnb-welcome-blocks">
66
+
67
+ <div class="cnb-block">
68
+ <h1>Why do I need an account?</h1>
69
+ <h3>With an account you enabable the cloud features from callnowbutton.com.</h3>
70
+ <p>Here's a close-up of some of the cloud features:</p>
71
+ <div class="cnb-divider"></div>
72
+
73
+ <h2>🎁 More actions and icons 🎁</h2>
74
+ <img class="cnb-width-80 cnb-extra-space" src="<?php echo esc_url(WP_PLUGIN_URL . '/' . CNB_BASEFOLDER . '/resources/images/cnb-icons-actions.png');?>" alt="WhatsApp modal">
75
+ <p>New actions include WhatsApp, SMS/Text, Email, Signal, Telegram, Messenger, Location, Link and Smooth scroll to point.</p>
76
+
77
+ <div class="cnb-divider"></div>
78
+
79
+ <h2>💬 WhatsApp chat modal 💬</h2>
80
+ <img src="<?php echo esc_url(WP_PLUGIN_URL . '/' . CNB_BASEFOLDER . '/resources/images/whatsapp-modal.png');?>" alt="WhatsApp modal">
81
+ <p>Start the WhatsApp conversation on your website.</p>
82
+
83
+ <div class="cnb-divider"></div>
84
+
85
+ <h2>💎 Multibutton 💎</h2>
86
+ <img class="cnb-width-80" src="<?php echo esc_url(WP_PLUGIN_URL . '/' . CNB_BASEFOLDER . '/resources/images/multibutton.png');?>" alt="Multibutton">
87
+ <p>Takes up little space but reaveals a treasure of options.</p>
88
+
89
+ <div class="cnb-divider"></div>
90
+
91
+ <h2>✨ Buttonbar ✨</h2>
92
+ <img class="cnb-width-80" src="<?php echo esc_url(WP_PLUGIN_URL . '/' . CNB_BASEFOLDER . '/resources/images/buttonbar.png');?>" alt="Buttonbar">
93
+ <p>Create a web app experience on your website.</p>
94
+
95
+ <div class="cnb-divider"></div>
96
+
97
+ <h2>🕘 The scheduler 🕔</h2>
98
+ <img src="<?php echo esc_url(WP_PLUGIN_URL . '/' . CNB_BASEFOLDER . '/resources/images/button-scheduler.png');?>" alt="The scheduler">
99
+ <p>Control exactly when your buttons are displayed. Maybe a call button during business hours and a mail buttons when you're closed.</p>
100
+
101
+ <div class="cnb-divider"></div>
102
+ <h3>And much more!</h3>
103
+ </div>
104
+ </div>
105
+ <div class="cnb-welcome-blocks">
106
+ <div class="cnb-block cnb-signup-box">
107
+ <h2>Create your free account and supercharge your Call Now Button.</h2>
108
+ <?php echo CnbHeaderNotices::cnb_settings_email_activation_input(); // phpcs:ignore WordPress.Security ?>
109
+ </div>
110
+ <div class="cnb-divider"></div>
111
+ <p><i>Only need a Call button? <a href="<?php echo esc_url( $link ) ?>">Continue without an account</a>.</i></p>
112
+ </div>
113
+ </div>
114
+ <?php }
115
+ }
src/admin/getting-started/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
src/admin/legacy/CnbLegacyController.php CHANGED
@@ -5,15 +5,6 @@ namespace cnb\admin\legacy;
5
  // don't load directly
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
8
- use cnb\notices\CnbAdminNotices;
9
-
10
  class CnbLegacyController {
11
 
12
- public function show_welcome_banner() {
13
- $dismiss_value = 'welcome-panel';
14
- $dismissed_option = CnbAdminNotices::get_instance()->get_dismiss_option_name( $dismiss_value );
15
- $is_dismissed = CnbAdminNotices::get_instance()->is_dismissed( $dismissed_option );
16
-
17
- return ! $is_dismissed;
18
- }
19
  }
5
  // don't load directly
6
  defined( 'ABSPATH' ) || die( '-1' );
7
 
 
 
8
  class CnbLegacyController {
9
 
 
 
 
 
 
 
 
10
  }
src/admin/legacy/CnbLegacyEdit.php CHANGED
@@ -17,7 +17,6 @@ class CnbLegacyEdit {
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' );
@@ -155,71 +154,6 @@ class CnbLegacyEdit {
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
- <p id="welcome-banner-notice">🎉 Welcome to <b>Call Now Button</b>! <a class="cnb-expand-link">Click to expand...</a></p>
181
- <div class="welcome-banner-content">
182
- <h2>Welcome to Call&nbsp;Now&nbsp;Button!</h2>
183
- <div class="welcome-banner-column-container">
184
- <div class="welcome-banner-column">
185
- <h3>Some cool stats!</h3>
186
- <div class="welcome-column-box">
187
- <p class="cnb-mobile-inline">🎉 The #1 click-to-call button on WordPress for 10 years!</p>
188
- <p class="cnb-mobile-inline">🚀 200k+ active installations and growing every day!</p>
189
- <p class="cnb-mobile-inline">❤️ Loved by our users and rated 4.9!</p>
190
- <p class="cnb-mobile-inline">💎 Call Now Button <strong>Premium</strong> has so much more!
191
- </p>
192
- </div>
193
- </div>
194
- <div class="welcome-banner-column">
195
- <h3>What's in Premium?</h3>
196
- <p class="cnb-mobile-inline">+ Create lots of buttons</p>
197
- <p class="cnb-mobile-inline">+ Phone, SMS, Email, Maps, Links, Anchors</p>
198
- <p class="cnb-mobile-inline">+ WhatsApp, Signal, Telegram, FB Messenger</p>
199
- <p class="cnb-mobile-inline">+ Multi action buttons</p>
200
- <p class="cnb-mobile-inline">+ Button scheduler</p>
201
- <p class="cnb-mobile-inline">And much more!</p>
202
- </div>
203
- <div class="welcome-banner-column">
204
- <a class="button button-primary button-hero" href="<?php echo esc_url( $upgrade_link ) ?>">Get
205
- Premium Free</a>
206
-
207
- <p><a href="<?php echo esc_url( $upgrade_link ) ?>">More info about Premium</a></p>
208
- <h3>Other resources</h3>
209
- <p>
210
- <a href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/', 'welcome-banner', 'Help center' ) ) ?>">Help
211
- center</a></p>
212
- <p>
213
- <a href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/#faq', 'welcome-banner', 'FAQ' ) ) ?>">FAQ</a>
214
- </p>
215
- </div>
216
- </div>
217
- </div>
218
- <button type="button" class="notice-dismiss"><span
219
- class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.' ) ?></span></button>
220
- </div>
221
- <?php }
222
-
223
  private function render_form() {
224
  $cnb_options = get_option( 'cnb' );
225
  $adminFunctions = new CnbAdminFunctions();
@@ -236,6 +170,8 @@ class CnbLegacyEdit {
236
  <a href="<?php echo esc_url( $this->create_tab_url( 'extra_options' ) ) ?>"
237
  class="nav-tab <?php echo esc_attr( $adminFunctions->is_active_tab( 'extra_options' ) ) ?>"
238
  data-tab-name="extra_options">Presentation</a>
 
 
239
  </h2>
240
 
241
  <form method="post" action="<?php echo esc_url( admin_url( 'options.php' ) ) ?>"
@@ -246,15 +182,39 @@ class CnbLegacyEdit {
246
  <tr>
247
  <th colspan="2"></th>
248
  </tr>
 
249
  <tr>
250
  <th scope="row"><label for="cnb-active">Button status</label></th>
 
251
  <td>
252
  <input type="hidden" name="cnb[active]" value="0"/>
253
- <input id="cnb-active" type="checkbox" name="cnb[active]"
254
- value="1" <?php checked( '1', $cnb_options['active'] ); ?>>
255
- <label for="cnb-active">Enable</label>
 
 
 
256
  </td>
257
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  <tr>
259
  <th scope="row"><label for="cnb-number">Phone number</label> <a
260
  href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/basics/phone-number/', 'legacy-basics-question-mark', 'phone-number' ) ) ?>"
@@ -396,15 +356,75 @@ class CnbLegacyEdit {
396
  value="exclude" <?php checked( 'exclude', $cnb_options['limit'] ); ?> />
397
  <label for="limit2">Exclude these posts and pages.</label>
398
  </div>
399
- <br>
400
- <div>
401
- <input type="hidden" name="cnb[frontpage]" value="0"/>
402
- <input id="frontpage" type="checkbox" name="cnb[frontpage]"
403
- value="1" <?php checked( '1', $cnb_options['frontpage'] ); ?>>
404
- <label title="right" for="frontpage">Hide button on front page</label>
405
- </div>
406
  </td>
407
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
  </table>
409
  <table class="form-table <?php echo esc_attr( $adminFunctions->is_active_tab( 'advanced_options' ) ) ?>">
410
  <tr>
@@ -437,10 +457,10 @@ class CnbLegacyEdit {
437
  <span class="cnb-purple">&check;</span> Change icons<br>
438
  <span class="cnb-purple">&check;</span> Button animations<br>
439
  <span class="cnb-purple">&check;</span> Live previews</p>
440
- <p>Get all of this and much more in <strong>Premium</strong></p>',
441
  'unlock',
442
  '',
443
- 'Get Premium Free',
444
  ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
445
  );
446
  ?>
@@ -456,8 +476,8 @@ class CnbLegacyEdit {
456
  &check; Smooth scroll anchors<br>
457
  &check; Links</p>',
458
  'format-chat',
459
- '<strong>It\'s all in Premium!</strong>',
460
- 'Learn more',
461
  ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
462
  );
463
  ?>
@@ -480,13 +500,27 @@ class CnbLegacyEdit {
480
  );
481
  ( new CnbAdminFunctions() )->cnb_promobox(
482
  'blue',
483
- 'Go Premium for FREE!',
484
- 'Premium adds a ton of extra power to the Call Now Button.</p>
485
- <p>The Premium Free plan shows a little branding with your buttons but gives you access to all features.</p>
486
  <p>Try it out and enjoy scheduling, multiple buttons, more button types, animations and much more!</p>',
487
  'money-alt',
488
  '',
489
- 'Try Premium',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
491
  );
492
  ?>
17
  add_action( 'cnb_header_name', array( $this, 'header' ) );
18
 
19
  do_action( 'cnb_header' );
 
20
  $this->render_form();
21
  do_action( 'cnb_footer' );
22
  do_action( 'cnb_finish' );
154
  echo esc_html( CNB_NAME ) . ' <span class="cnb-version">v' . esc_html( CNB_VERSION ) . '</span>';
155
  }
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  private function render_form() {
158
  $cnb_options = get_option( 'cnb' );
159
  $adminFunctions = new CnbAdminFunctions();
170
  <a href="<?php echo esc_url( $this->create_tab_url( 'extra_options' ) ) ?>"
171
  class="nav-tab <?php echo esc_attr( $adminFunctions->is_active_tab( 'extra_options' ) ) ?>"
172
  data-tab-name="extra_options">Presentation</a>
173
+ <a href="<?php echo esc_url( $this->create_tab_url( 'scheduler' ) ) ?>" class="nav-tab <?php echo esc_attr( $adminFunctions->is_active_tab( 'scheduler' ) ) ?> cnb_disabled_feature"
174
+ data-tab-name="scheduler"><span class="dashicons dashicons-lock"></span> Scheduler</a>
175
  </h2>
176
 
177
  <form method="post" action="<?php echo esc_url( admin_url( 'options.php' ) ) ?>"
182
  <tr>
183
  <th colspan="2"></th>
184
  </tr>
185
+
186
  <tr>
187
  <th scope="row"><label for="cnb-active">Button status</label></th>
188
+
189
  <td>
190
  <input type="hidden" name="cnb[active]" value="0"/>
191
+ <input id="cnb-active" class="cnb_toggle_checkbox" type="checkbox" name="cnb[active]"
192
+ value="1" <?php checked( '1', $cnb_options['active'] ); ?> />
193
+ <label for="cnb-active" class="cnb_toggle_label">Toggle</label>
194
+ <span data-cnb_toggle_state_label="cnb-active" class="cnb_toggle_state cnb_toggle_false">(Inactive)</span>
195
+ <span data-cnb_toggle_state_label="cnb-active"
196
+ class="cnb_toggle_state cnb_toggle_true">Active</span>
197
  </td>
198
  </tr>
199
+ <tr>
200
+ <th scope="row"><label for="cnb_action_type">Button type</label></th>
201
+ <td>
202
+ <select>
203
+ <option selected="selected">Phone</option>
204
+ <option disabled>Email*</option>
205
+ <option disabled>SMS/Text*</option>
206
+ <option disabled>WhatsApp*</option>
207
+ <option disabled>Messenger*</option>
208
+ <option disabled>Signal*</option>
209
+ <option disabled>Telegram*</option>
210
+ <option disabled>Link*</option>
211
+ <option disabled>Google Maps*</option>
212
+ <option disabled>Anchor*</option>
213
+ </select>
214
+ <p class="description">*<a href="<?php echo esc_url(( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()); ?>">Create an account</a> to enable these actions</p>
215
+ </td>
216
+ </tr>
217
+
218
  <tr>
219
  <th scope="row"><label for="cnb-number">Phone number</label> <a
220
  href="<?php echo esc_url( $cnb_utils->get_support_url( 'wordpress-free/basics/phone-number/', 'legacy-basics-question-mark', 'phone-number' ) ) ?>"
356
  value="exclude" <?php checked( 'exclude', $cnb_options['limit'] ); ?> />
357
  <label for="limit2">Exclude these posts and pages.</label>
358
  </div>
 
 
 
 
 
 
 
359
  </td>
360
  </tr>
361
+ <tr class="appearance">
362
+ <th scope="row"><label for="cnb-show-fp">Show button on front page</label></th>
363
+ <td>
364
+ <input type="hidden" name="cnb[frontpage]" value="1"/>
365
+ <input id="cnb-show-fp" class="cnb_toggle_checkbox" type="checkbox" name="cnb[frontpage]"
366
+ value="0" <?php checked( '0', $cnb_options['frontpage'] ); ?> />
367
+ <label for="cnb-show-fp" class="cnb_toggle_label">Toggle</label>
368
+ <span data-cnb_toggle_state_label="cnb-show-fp" class="cnb_toggle_state cnb_toggle_false">(No)</span>
369
+ <span data-cnb_toggle_state_label="cnb-show-fp"
370
+ class="cnb_toggle_state cnb_toggle_true">Yes</span>
371
+ </td>
372
+ </tr>
373
+
374
+
375
+
376
+ </table>
377
+ <table class="form-table <?php echo esc_attr( $adminFunctions->is_active_tab( 'scheduler' ) ) ?>" data-tab-name="scheduler">
378
+ <tr>
379
+ <th colspan="2"><a href="<?php echo esc_url(( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()); ?>">Create an account</a> to enable the scheduler.</th>
380
+ </tr>
381
+ <tr class="cnb_disabled_feature">
382
+ <th scope="row">Show at all times</th>
383
+ <td>
384
+ <input class="cnb_toggle_checkbox" type="checkbox" checked="checked" disabled>
385
+ <label for="actions_schedule_show_always" class="cnb_toggle_label" style="background-color:#b3afaf">Toggle</label>
386
+ <span data-cnb_toggle_state_label="actions_schedule_show_always" class="cnb_toggle_state cnb_toggle_true">Yes</span>
387
+ <span data-cnb_toggle_state_label="actions_schedule_show_always" class="cnb_toggle_state cnb_toggle_false">(No)</span>
388
+ </td>
389
+ </tr>
390
+ <tr class="cnb_disabled_feature">
391
+ <th>Set days</th>
392
+ <td>
393
+ <input disabled class="cnb_day_selector" id="cnb_weekday_0" type="checkbox">
394
+ <label title="Monday" class="cnb_day_selector" for="cnb_weekday_0">Mon</label>
395
+
396
+ <input disabled class="cnb_day_selector" id="cnb_weekday_1" type="checkbox">
397
+ <label title="Tuesday" class="cnb_day_selector" for="cnb_weekday_1">Tue</label>
398
+
399
+ <input disabled class="cnb_day_selector" id="cnb_weekday_2" type="checkbox">
400
+ <label title="Wednesday" class="cnb_day_selector" for="cnb_weekday_2">Wed</label>
401
+
402
+ <input disabled class="cnb_day_selector" id="cnb_weekday_3" type="checkbox">
403
+ <label title="Thursday" class="cnb_day_selector" for="cnb_weekday_3">Thu</label>
404
+
405
+ <input disabled class="cnb_day_selector" id="cnb_weekday_4" type="checkbox">
406
+ <label title="Friday" class="cnb_day_selector" for="cnb_weekday_4">Fri</label>
407
+
408
+ <input disabled class="cnb_day_selector" id="cnb_weekday_5" type="checkbox">
409
+ <label title="Saturday" class="cnb_day_selector" for="cnb_weekday_5">Sat</label>
410
+
411
+ <input disabled class="cnb_day_selector" id="cnb_weekday_6" type="checkbox">
412
+ <label title="Sunday" class="cnb_day_selector" for="cnb_weekday_6">Sun</label>
413
+ </td>
414
+ </tr>
415
+ <tr class="cnb_disabled_feature">
416
+ <th><label for="actions_schedule_outside_hours">After hours</label></th>
417
+ <td>
418
+ <input id="actions_schedule_outside_hours" disabled class="cnb_toggle_checkbox" type="checkbox">
419
+ <label for="actions_schedule_outside_hours" class="cnb_toggle_label">Toggle</label>
420
+ </td>
421
+ </tr>
422
+ <tr class="cnb_disabled_feature">
423
+ <th>Set times</th>
424
+ <td class="cnb-scheduler-slider">
425
+ <p id="cnb-schedule-range-text">From <strong>8:00 am</strong> till <strong>5:00 pm</strong></p>
426
+ </td>
427
+ </tr>
428
  </table>
429
  <table class="form-table <?php echo esc_attr( $adminFunctions->is_active_tab( 'advanced_options' ) ) ?>">
430
  <tr>
457
  <span class="cnb-purple">&check;</span> Change icons<br>
458
  <span class="cnb-purple">&check;</span> Button animations<br>
459
  <span class="cnb-purple">&check;</span> Live previews</p>
460
+ <p>Enable <strong>Cloud</strong> to get all of this and more!</p>',
461
  'unlock',
462
  '',
463
+ 'Create account',
464
  ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
465
  );
466
  ?>
476
  &check; Smooth scroll anchors<br>
477
  &check; Links</p>',
478
  'format-chat',
479
+ '<strong>More everything!</strong>',
480
+ 'Discover Cloud',
481
  ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
482
  );
483
  ?>
500
  );
501
  ( new CnbAdminFunctions() )->cnb_promobox(
502
  'blue',
503
+ 'Get more for FREE!',
504
+ 'Cloud adds a ton of extra power to the Call Now Button.</p>
505
+ <p>The free Cloud plan shows a little branding with your buttons and gives you full access to 99% of all features.</p>
506
  <p>Try it out and enjoy scheduling, multiple buttons, more button types, animations and much more!</p>',
507
  'money-alt',
508
  '',
509
+ 'Try Cloud',
510
+ ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
511
+ );
512
+ ?>
513
+ </div>
514
+ <div class="cnb-on-active-tab <?php echo esc_attr( $adminFunctions->is_active_tab( 'scheduler' ) ) ?>">
515
+ <?php
516
+ ( new CnbAdminFunctions() )->cnb_promobox(
517
+ 'purple',
518
+ 'Phones off at 6pm?',
519
+ '<p>Sign up to enable a scheduler that allows you to set the days and hours that you are available.</p>' .
520
+ '<p>You can even replace it with an email button during your off-hours so people can still contact you.</p>',
521
+ 'clock',
522
+ '<strong>Use the scheduler!</strong>',
523
+ 'Enable cloud',
524
  ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
525
  );
526
  ?>
src/admin/legacy/CnbLegacyUpgrade.php CHANGED
@@ -11,16 +11,16 @@ use cnb\utils\CnbUtils;
11
 
12
  class CnbLegacyUpgrade {
13
  function header() {
14
- echo 'Unlock extra features';
15
  }
16
 
17
- function standard_plugin_promobox() {
18
  ?>
19
  <div class="cnb-body-column hide-on-mobile">
20
  <?php
21
  ( new CnbAdminFunctions() )->cnb_promobox(
22
  'grey',
23
- 'Standard plugin',
24
  '<p>&check; One button<br>
25
  &check; Phone<br><br>
26
  &check; Circular (single action)<br>
@@ -48,7 +48,7 @@ class CnbLegacyUpgrade {
48
  &check; Flexible z-index<br>
49
  &nbsp;
50
  </p>',
51
- 'admin-plugins',
52
  '<strong>Free</strong>',
53
  'Currently active',
54
  'disabled'
@@ -57,14 +57,14 @@ class CnbLegacyUpgrade {
57
  </div>
58
  <?php }
59
 
60
- function premium_plugin_promobox() {
61
  $cnb_utils = new CnbUtils();
62
  ?>
63
  <div class="cnb-body-column">
64
  <?php
65
  ( new CnbAdminFunctions() )->cnb_promobox(
66
  'purple',
67
- 'Premium',
68
  '
69
  <p><strong>&check; Lots of buttons!</strong><br>
70
  &check; Phone, SMS/Text, Email, Maps, URLs, Anchors (with smooth scroll)<br>
@@ -106,25 +106,30 @@ class CnbLegacyUpgrade {
106
  function upgrade_faq() { ?>
107
  <div style="max-width:600px;margin:0 auto">
108
  <h1 class="cnb-center">FAQ</h1>
109
- <h3>Can I really get Premium for Free?</h3>
110
- <p>Yes. You can use all premium features of the Call Now Button for free. No credit card is required. You
111
- only need an account for that. The difference with the paid Premium plans is that a small "Powered by"
112
  notice is added to your buttons.</p>
113
- <h3>Does the Premium plan require an account?</h3>
 
 
114
  <p>Yes. We want the Call Now Button to be accessible to all website owners. Even those that do not have a
115
- WordPress powered website. The Premium version of the Call Now Button can be used by everyone. You can
116
  continue to manage your buttons from your WordPress instance, but you could also do this via our web
117
- app. And should you ever move to a different CMS, your button(s) will just move with you.</p>
118
- <h3>What is the "powered by" notice on the Free Premium plan?</h3>
119
- <p>Call Now Button Premium is available for a small yearly or annual fee, but it is also possible to get it
120
- for <em>free</em>. The free option introduces a small notice to your buttons that says "Powered by Call
121
  Now Button". It's very delicate and will not distract the the visitor from your content.</p>
 
 
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' );
@@ -133,8 +138,8 @@ class CnbLegacyUpgrade {
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>
11
 
12
  class CnbLegacyUpgrade {
13
  function header() {
14
+ echo 'Activate Cloud to unlock lots of extra features';
15
  }
16
 
17
+ private function standard_plugin_promobox() {
18
  ?>
19
  <div class="cnb-body-column hide-on-mobile">
20
  <?php
21
  ( new CnbAdminFunctions() )->cnb_promobox(
22
  'grey',
23
+ 'Lite',
24
  '<p>&check; One button<br>
25
  &check; Phone<br><br>
26
  &check; Circular (single action)<br>
48
  &check; Flexible z-index<br>
49
  &nbsp;
50
  </p>',
51
+ 'database',
52
  '<strong>Free</strong>',
53
  'Currently active',
54
  'disabled'
57
  </div>
58
  <?php }
59
 
60
+ private function premium_plugin_promobox() {
61
  $cnb_utils = new CnbUtils();
62
  ?>
63
  <div class="cnb-body-column">
64
  <?php
65
  ( new CnbAdminFunctions() )->cnb_promobox(
66
  'purple',
67
+ 'Cloud',
68
  '
69
  <p><strong>&check; Lots of buttons!</strong><br>
70
  &check; Phone, SMS/Text, Email, Maps, URLs, Anchors (with smooth scroll)<br>
106
  function upgrade_faq() { ?>
107
  <div style="max-width:600px;margin:0 auto">
108
  <h1 class="cnb-center">FAQ</h1>
109
+ <h3>Is Cloud really free?</h3>
110
+ <p>Yes. You can use 99% of the cloud features of the Call Now Button for free. No credit card is required. You
111
+ only need an account for that. The difference with the PRO plan is that a small "Powered by"
112
  notice is added to your buttons.</p>
113
+ <h3>What's included in PRO?</h3>
114
+ <p>Upgrading to PRO removes the "Powered by Call Now Button" notice and adds some Premium features such as geo targeting and setting a scroll depth for a button to appear.</p>
115
+ <h3>Does Cloud require an account?</h3>
116
  <p>Yes. We want the Call Now Button to be accessible to all website owners. Even those that do not have a
117
+ WordPress powered website. The Cloud version of the Call Now Button can be used by everyone on any website. You can
118
  continue to manage your buttons from your WordPress instance, but you could also do this via our web
119
+ app on <a href="https://callnowbutton.com" target="_blank">callnowbutton.com</a>. And should you ever move to a different CMS, your button(s) will just move with you.</p>
120
+ <h3>What is the "powered by" notice?</h3>
121
+ <p>The cloud version of Call Now Button is available for a small yearly or monthly fee, but there is also a
122
+ <em>free</em> option. The free option introduces a small notice to your buttons that says "Powered by Call
123
  Now Button". It's very delicate and will not distract the the visitor from your content.</p>
124
+ <h3>Why is it called Cloud?</h3>
125
+ <p>It's called Cloud because it is served from remote servers (the cloud) and no longer stored locally on your website. Therefore you need an account to enabled it. WordPress is currently the only platform that has its own interface so you can still manage your buttons from inside WordPress. Other platforms can use the Call Now Button as well but manage their buttons via the web app on app.callnowbutton.com.</p>
126
  </div>
127
  <?php }
128
 
129
  public function render() {
130
  do_action( 'cnb_init', __METHOD__ );
131
  wp_enqueue_script( CNB_SLUG . '-settings' );
132
+ wp_enqueue_script( CNB_SLUG . '-premium-activation' );
133
 
134
  add_action( 'cnb_header_name', array( $this, 'header' ) );
135
  do_action( 'cnb_header' );
138
  <div class="cnb-one-column-section">
139
  <div class="cnb-body-content">
140
  <div class="cnb-two-promobox-row">
 
141
  <?php $this->premium_plugin_promobox() ?>
142
+ <?php $this->standard_plugin_promobox() ?>
143
  </div>
144
  <?php $this->upgrade_faq() ?>
145
  </div>
src/admin/partials/CnbFooter.php CHANGED
@@ -43,8 +43,8 @@ class CnbFooter {
43
  <p class="cnb-url cnb-center"><a
44
  href="<?php echo esc_url( $cnb_utils->get_website_url( '', 'footer-links', 'branding' ) ) ?>"
45
  target="_blank">Call Now Button<?php if ( $cnb_utils->isCloudActive( $cnb_options ) ) {
46
- echo '<span class="cnb_footer_beta">PREMIUM</span>';
47
- } ?></a></p>
48
  <p class="cnb-center">Version <?php echo esc_attr( CNB_VERSION ) ?>
49
  <p class="cnb-center cnb-spacing">
50
  <a href="<?php echo esc_url( $cnb_utils->get_support_url( '', 'footer-links', 'support' ) ) ?>"
@@ -54,7 +54,7 @@ class CnbFooter {
54
  target="_blank" title="Feature Requests">Suggestions</a>
55
  <?php if ( ! $cnb_utils->isCloudActive( $cnb_options ) ) { ?>
56
  &middot; <strong><a href="<?php echo esc_url( $upgrade_link ) ?>"
57
- title="Unlock features">Upgrade</a></strong>
58
  <?php } ?>
59
  </p>
60
  </div>
@@ -74,7 +74,7 @@ class CnbFooter {
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"
43
  <p class="cnb-url cnb-center"><a
44
  href="<?php echo esc_url( $cnb_utils->get_website_url( '', 'footer-links', 'branding' ) ) ?>"
45
  target="_blank">Call Now Button<?php if ( $cnb_utils->isCloudActive( $cnb_options ) ) {
46
+ echo '<span class="cnb_footer_beta">CLOUD</span>';
47
+ } else { echo '<span class="cnb_footer_beta">Lite</span>'; } ?></a></p>
48
  <p class="cnb-center">Version <?php echo esc_attr( CNB_VERSION ) ?>
49
  <p class="cnb-center cnb-spacing">
50
  <a href="<?php echo esc_url( $cnb_utils->get_support_url( '', 'footer-links', 'support' ) ) ?>"
54
  target="_blank" title="Feature Requests">Suggestions</a>
55
  <?php if ( ! $cnb_utils->isCloudActive( $cnb_options ) ) { ?>
56
  &middot; <strong><a href="<?php echo esc_url( $upgrade_link ) ?>"
57
+ title="Unlock features">Enable cloud</a></strong>
58
  <?php } ?>
59
  </p>
60
  </div>
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"
src/admin/partials/CnbHeaderNotices.php CHANGED
@@ -82,8 +82,8 @@ class CnbHeaderNotices {
82
  $register_url = $cnb_utils->get_app_url( 'register', 'upgrade-to-premium-options', 'callnowbutton.com' );
83
  $url = $cnb_utils->get_app_url( '', 'manual_activation', 'sign-up-for-api' );
84
 
85
- $message = '<h3 class="title cnb-remove-add-new">Activating Premium</h3>';
86
- $message .= '<p>To activate Premium, you\'ll need a <a href="' . esc_url( $register_url ) . '" target="_blank">callnowbutton.com</a> account and an API key. There\'s 2 ways to do this:</p>';
87
  $message .= '<h4>Option 1: Email activation (easy and fast!)</h4>';
88
  $message .= self::cnb_settings_email_activation_input();
89
 
@@ -122,18 +122,23 @@ class CnbHeaderNotices {
122
  $adminNotices->warning( $message );
123
  }
124
 
 
 
 
 
 
 
 
125
  public static function cnb_settings_email_activation_input() {
126
  $cnb_utils = new CnbUtils();
127
- $terms_url = $cnb_utils->get_website_url( 'terms.html', 'email-activation', 'terms' );
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>';
134
- $message .= '<p id="cnb_email_activation"></p>';
135
-
136
- $message .= '<p class="nonessential">By clicking <u>Activate Premium</u> an account will be created with your email address on callnowbutton.com and you agree to our <a href="' . esc_url( $terms_url ) . '" target="_blank">Terms & Conditions</a> and <a href="' . esc_url( $privacy_url ) . '" target="_blank">Privacy statement</a>.</p>';
137
  $message .= '</form>';
138
 
139
  return $message;
@@ -143,7 +148,7 @@ class CnbHeaderNotices {
143
  $message = sprintf( '<form action="%1$s" class="cnb-container">', esc_url( admin_url( 'admin.php' ) ) );
144
  $message .= '<input type="hidden" name="page" value="call-now-button-settings" />';
145
  $message .= '<div>';
146
- $message .= '<input type="text" class="cnb_activation_input_field" name="api_key" placeholder="Paste API key here"/>';
147
  $message .= get_submit_button( __( 'Store API key' ), 'primary', 'submit', false );
148
  $message .= '</div>';
149
  $message .= '</form>';
@@ -387,7 +392,7 @@ class CnbHeaderNotices {
387
  ),
388
  $url );
389
  $redirect_url = esc_url( $redirect_link );
390
- $message = "<p id='cnb-notice-domain-timezone-unsupported'>Please fix your timezone in the ";
391
  $message .= '<a href="' . $redirect_url . '">Advanced settings</a> tab ';
392
  $message .= 'to avoid unpredictable behavior when using the scheduler.</p>';
393
  CnbAdminNotices::get_instance()->warning( $message );
82
  $register_url = $cnb_utils->get_app_url( 'register', 'upgrade-to-premium-options', 'callnowbutton.com' );
83
  $url = $cnb_utils->get_app_url( '', 'manual_activation', 'sign-up-for-api' );
84
 
85
+ $message = '<h3 class="title cnb-remove-add-new">Enable cloud features</h3>';
86
+ $message .= '<p>To enable cloud features, you\'ll need a <a href="' . esc_url( $register_url ) . '" target="_blank">callnowbutton.com</a> account and an API key. There\'s 2 ways to do this:</p>';
87
  $message .= '<h4>Option 1: Email activation (easy and fast!)</h4>';
88
  $message .= self::cnb_settings_email_activation_input();
89
 
122
  $adminNotices->warning( $message );
123
  }
124
 
125
+ /**
126
+ * Returns an HTML form containing all the fields needed for a Premium signup.
127
+ *
128
+ * The returned string is already pre-HTML-escaped.
129
+ *
130
+ * @return string HTML form with e-mail placeholder and a Submit button
131
+ */
132
  public static function cnb_settings_email_activation_input() {
133
  $cnb_utils = new CnbUtils();
134
+ $terms_url = $cnb_utils->get_website_url( 'legal/terms/', 'email-activation', 'terms' );
135
+ $privacy_url = $cnb_utils->get_website_url( 'legal/privacy/', 'email-activation', 'privacy' );
136
+ $message = '<form class="cnb-container cnb_email_activation">';
137
+ $message .= '<input type="text" required="required" class="cnb_activation_input_field" name="cnb_email_activation_address" placeholder="Your email address" /> ';
138
+ $message .= get_submit_button( __( 'Create account' ), 'primary', 'cnb_email_activation_submit', false );
139
+ $message .= '<p class="cnb_email_activation_message"></p>';
140
+
141
+ $message .= '<p class="nonessential">By clicking <u>Create account</u> an account will be created with your email address on callnowbutton.com and you agree to our <a href="' . esc_url( $terms_url ) . '" target="_blank">Terms & Conditions</a> and <a href="' . esc_url( $privacy_url ) . '" target="_blank">Privacy statement</a>.</p>';
 
 
142
  $message .= '</form>';
143
 
144
  return $message;
148
  $message = sprintf( '<form action="%1$s" class="cnb-container">', esc_url( admin_url( 'admin.php' ) ) );
149
  $message .= '<input type="hidden" name="page" value="call-now-button-settings" />';
150
  $message .= '<div>';
151
+ $message .= '<input type="text" required="required" class="cnb_activation_input_field" name="api_key" placeholder="Paste API key here"/>';
152
  $message .= get_submit_button( __( 'Store API key' ), 'primary', 'submit', false );
153
  $message .= '</div>';
154
  $message .= '</form>';
392
  ),
393
  $url );
394
  $redirect_url = esc_url( $redirect_link );
395
+ $message = "<p class='cnb-notice-domain-timezone-unsupported'>Please fix your timezone in the ";
396
  $message .= '<a href="' . $redirect_url . '">Advanced settings</a> tab ';
397
  $message .= 'to avoid unpredictable behavior when using the scheduler.</p>';
398
  CnbAdminNotices::get_instance()->warning( $message );
src/admin/settings/CnbApiKeyActivatedView.php CHANGED
@@ -19,7 +19,7 @@ class CnbApiKeyActivatedView {
19
  private $activation;
20
 
21
  function header() {
22
- echo 'Premium activation';
23
  }
24
 
25
  /**
@@ -97,7 +97,7 @@ class CnbApiKeyActivatedView {
97
  echo '<div style="width:200px;margin: 0 auto;">';
98
  ( new CnbDomainViewUpgradeFinished() )->echoBigYaySvg();
99
  echo '</div>';
100
- echo '<h1>You have successfully upgraded to Call Now Button PREMIUM</h1>';
101
  echo '</div>';
102
  }
103
 
@@ -157,7 +157,7 @@ class CnbApiKeyActivatedView {
157
  }
158
  echo '<div class="cnb-plan-features cnb-center top-50">';
159
  echo '<h1><strong>Upgrade to PRO!</strong></h1>';
160
- echo '<h3>🤩 All features from Premium plus more! 🤩</h3>';
161
  ( new CnbDomainViewUpgradeOverview() )->renderUpgradeForm( $domain );
162
  echo '</div>';
163
  }
@@ -169,12 +169,12 @@ class CnbApiKeyActivatedView {
169
  */
170
  private function renderActivationFailure( $user ) {
171
  if ( ! is_wp_error( $user ) ) {
172
- echo '<div style="text-align: center"><h2>Premium is already active</h2></div>';
173
 
174
  return;
175
  }
176
 
177
- echo '<h1>You tried to activate the Premium version, but something went wrong.</h1>';
178
  }
179
 
180
  private function renderBenefits() {
@@ -190,7 +190,7 @@ class CnbApiKeyActivatedView {
190
  $this->renderActivationSuccess();
191
  }
192
  if ( ! $this->activation->success && ! is_wp_error( $user ) ) {
193
- echo '<div style="text-align: center"><h1>Call Now Button Premium is already active</h1></div>';
194
  }
195
  if ( $this->activation->success || ! is_wp_error( $user ) ) {
196
  $this->renderBenefits();
19
  private $activation;
20
 
21
  function header() {
22
+ echo 'Cloud activation';
23
  }
24
 
25
  /**
97
  echo '<div style="width:200px;margin: 0 auto;">';
98
  ( new CnbDomainViewUpgradeFinished() )->echoBigYaySvg();
99
  echo '</div>';
100
+ echo '<h1>You have successfully activated Call Now Button CLOUD</h1>';
101
  echo '</div>';
102
  }
103
 
157
  }
158
  echo '<div class="cnb-plan-features cnb-center top-50">';
159
  echo '<h1><strong>Upgrade to PRO!</strong></h1>';
160
+ echo '<h3>🤩 All features from Cloud plus more! 🤩</h3>';
161
  ( new CnbDomainViewUpgradeOverview() )->renderUpgradeForm( $domain );
162
  echo '</div>';
163
  }
169
  */
170
  private function renderActivationFailure( $user ) {
171
  if ( ! is_wp_error( $user ) ) {
172
+ echo '<div style="text-align: center"><h2>Cloud is already active</h2></div>';
173
 
174
  return;
175
  }
176
 
177
+ echo '<h1>You tried to activate CLOUD, but something went wrong.</h1>';
178
  }
179
 
180
  private function renderBenefits() {
190
  $this->renderActivationSuccess();
191
  }
192
  if ( ! $this->activation->success && ! is_wp_error( $user ) ) {
193
+ echo '<div style="text-align: center"><h1>Cloud is already active</h1></div>';
194
  }
195
  if ( $this->activation->success || ! is_wp_error( $user ) ) {
196
  $this->renderBenefits();
src/admin/settings/CnbSettingsController.php CHANGED
@@ -47,7 +47,7 @@ class CnbSettingsController {
47
  'show_all_buttons_for_domain' => 0,
48
  'footer_show_traces' => 0,
49
  'api_caching' => 0,
50
-
51
  );
52
 
53
  return $this->post_option_cnb( $defaults );
@@ -175,7 +175,13 @@ class CnbSettingsController {
175
  }
176
  }
177
 
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 );
@@ -191,10 +197,10 @@ class CnbSettingsController {
191
  // But just in case, this is here for other unseen errors..
192
  $messages[] = CnbAdminCloud::cnb_admin_get_error_message( 'save', 'settings', $check );
193
  }
194
- } else if ( $version_upgrade ) {
195
- // NOOP - Do nothing for a version upgrade
196
  } else {
197
- $messages[] = new CnbNotice( 'success', '<p>Your settings have been updated!</p>' );
 
 
198
  }
199
 
200
  $this->update_user_email_opt_in($original_settings, $input);
@@ -211,6 +217,17 @@ class CnbSettingsController {
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;
@@ -318,7 +335,7 @@ class CnbSettingsController {
318
  // set "migration done"
319
  // We should really only do this once, so we need to save something in the settings to stop continuous migration.
320
  add_option( 'cnb_cloud_migration_done', true );
321
- CnbAdminNotices::get_instance()->success( '<p>Successfully connected to your Call Now Button account.</p>' );
322
  }
323
 
324
  // If an API key was passed (no matter the status of activation)
47
  'show_all_buttons_for_domain' => 0,
48
  'footer_show_traces' => 0,
49
  'api_caching' => 0,
50
+ 'reporting_enabled' => 0,
51
  );
52
 
53
  return $this->post_option_cnb( $defaults );
175
  }
176
  }
177
 
178
+ $show_updated_notice = true;
179
+ if($this->is_version_upgrade($original_settings, $updated_options)) {
180
+ $show_updated_notice = false;
181
+ }
182
+ if (array_key_exists('activation_time', $input)) {
183
+ $show_updated_notice = false;
184
+ }
185
 
186
  // Check for legacy button
187
  $check = $this->disallow_active_without_phone_number( $updated_options );
197
  // But just in case, this is here for other unseen errors..
198
  $messages[] = CnbAdminCloud::cnb_admin_get_error_message( 'save', 'settings', $check );
199
  }
 
 
200
  } else {
201
+ if ($show_updated_notice) {
202
+ $messages[] = new CnbNotice( 'success', '<p>Your settings have been updated!</p>' );
203
+ }
204
  }
205
 
206
  $this->update_user_email_opt_in($original_settings, $input);
217
  return $updated_options;
218
  }
219
 
220
+ private function is_version_upgrade(&$original_settings, &$updated_options) {
221
+ if (!key_exists('changelog_version', $original_settings)) {
222
+ $original_settings['changelog_version'] = CNB_VERSION;
223
+ }
224
+ if (!key_exists('changelog_version', $updated_options)) {
225
+ $updated_options['changelog_version'] = CNB_VERSION;
226
+ }
227
+ return $original_settings['version'] != $updated_options['version'] || $original_settings['changelog_version'] != $updated_options['changelog_version'];
228
+
229
+ }
230
+
231
  private function update_user_email_opt_in($current, $new) {
232
  if (!key_exists('user_marketing_email_opt_in', $new)) {
233
  return;
335
  // set "migration done"
336
  // We should really only do this once, so we need to save something in the settings to stop continuous migration.
337
  add_option( 'cnb_cloud_migration_done', true );
338
+ CnbAdminNotices::get_instance()->success( '<p>Successfully connected to your Call Now Button cloud account.</p>' );
339
  }
340
 
341
  // If an API key was passed (no matter the status of activation)
src/admin/settings/CnbSettingsViewEdit.php CHANGED
@@ -55,7 +55,7 @@ class CnbSettingsViewEdit {
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>
@@ -66,7 +66,7 @@ class CnbSettingsViewEdit {
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>
@@ -158,7 +158,7 @@ class CnbSettingsViewEdit {
158
  but it seems to be invalid or outdated.</p>
159
  <p class="description">Clicking "Disconnect account" will drop the API key
160
  and disconnect the plugin from your account. You will lose access to
161
- your buttons and Premium functionality until you reconnect with a
162
  callnowbutton.com account.
163
  <br>
164
  <input type="button" name="cnb_api_key_delete" id="cnb_api_key_delete"
@@ -177,7 +177,7 @@ class CnbSettingsViewEdit {
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"/>
@@ -357,14 +357,14 @@ class CnbSettingsViewEdit {
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(
364
  'green',
365
  'More buttons!',
366
- 'Switch to Premium to enable lots of buttons. Coupled with advanced page selection options you can get really creative.</p>' .
367
- '<p>If you need more phone numbers on a single page, then the Multibutton&trade; and the Buttonbar&trade; give you exactly what you need.</p>',
368
  'cloud',
369
  '<strong>Try it out!</strong>',
370
  'Learn more',
@@ -372,9 +372,9 @@ class CnbSettingsViewEdit {
372
  );
373
  ( new CnbAdminFunctions() )->cnb_promobox(
374
  'brown',
375
- 'Get Premium for FREE!',
376
- 'Premium adds a ton of extra power to the Call Now Button.</p>' .
377
- '<p>The Premium Free plan shows a little branding with your buttons but gives you access to all features.</p>' .
378
  '<p>Try it out and enjoy scheduling, multiple buttons, more button types, animations and much more!</p>',
379
  'money-alt',
380
  '',
@@ -411,7 +411,7 @@ class CnbSettingsViewEdit {
411
  </tr>
412
  <tr>
413
  <th scope="row">
414
- <label for="cnb_cloud_enabled">Premium
415
  <?php if ( $cnb_options['cloud_enabled'] == 0 ) { ?>
416
  <a href="<?php echo esc_url( ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page() ) ?>"
417
  class="cnb-nounderscore">
@@ -425,13 +425,13 @@ class CnbSettingsViewEdit {
425
  <input id="cnb_cloud_enabled" class="cnb_toggle_checkbox" name="cnb[cloud_enabled]"
426
  type="checkbox"
427
  value="1" <?php checked( '1', $cnb_options['cloud_enabled'] ); ?> />
428
- <label for="cnb_cloud_enabled" class="cnb_toggle_label">Enable Premium</label>
429
  <span data-cnb_toggle_state_label="cnb_cloud_enabled"
430
  class="cnb_toggle_state cnb_toggle_false">(Inactive)</span>
431
  <span data-cnb_toggle_state_label="cnb_cloud_enabled"
432
  class="cnb_toggle_state cnb_toggle_true">Active</span>
433
  <?php if ( $cnb_options['cloud_enabled'] == 0 ) { ?>
434
- <p class="description">Free and paid options available.
435
  <a href="<?php echo esc_url( ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page() ) ?>">Learn
436
  more</a>
437
  </p>
@@ -454,6 +454,7 @@ class CnbSettingsViewEdit {
454
  $adminFunctions = new CnbAdminFunctions();
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
 
@@ -476,17 +477,7 @@ class CnbSettingsViewEdit {
476
 
477
  do_action( 'cnb_header' );
478
 
479
- $cloud_successful = $status === 'cloud' && isset( $cnb_cloud_domain ) && ! ( $cnb_cloud_domain instanceof WP_Error );
480
- if ( ! $cloud_successful ) { ?>
481
- <script>
482
- jQuery(() => {
483
- const counter = jQuery("#cnb-nav-counter")
484
- if (counter.length && counter.text() === '!') {
485
- counter.hide();
486
- }
487
- });
488
- </script>
489
- <?php } ?>
490
 
491
  <div class="cnb-two-column-section">
492
  <div class="cnb-body-column">
55
  }
56
 
57
  private function render_error_reporting_options() {
58
+ $cnb_utils = new CnbUtils();
59
  ?>
60
  <tr>
61
  <th colspan="2"><h2>Miscellaneous</h2></th>
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( $cnb_utils->is_reporting_enabled() ); ?> />
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>
158
  but it seems to be invalid or outdated.</p>
159
  <p class="description">Clicking "Disconnect account" will drop the API key
160
  and disconnect the plugin from your account. You will lose access to
161
+ your buttons and cloud functionality until you reconnect with a
162
  callnowbutton.com account.
163
  <br>
164
  <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 all cloud 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"/>
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
+ 'Enable cloud',
361
  ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page()
362
  );
363
  ( new CnbAdminFunctions() )->cnb_promobox(
364
  'green',
365
  'More buttons!',
366
+ 'Enable cloud to get lots of extra buttons. Coupled with advanced page selection options you can get really creative.</p>' .
367
+ '<p>If you need more phone numbers on a single page, then the Multibutton&trade; and the Buttonbar&trade; give you excellent options.</p>',
368
  'cloud',
369
  '<strong>Try it out!</strong>',
370
  'Learn more',
372
  );
373
  ( new CnbAdminFunctions() )->cnb_promobox(
374
  'brown',
375
+ 'Add more features!',
376
+ 'Cloud adds a ton of extra power to the Call Now Button.</p>' .
377
+ '<p>The Free plan shows a little branding with your buttons but gives you access to 99% of all cloud features.</p>' .
378
  '<p>Try it out and enjoy scheduling, multiple buttons, more button types, animations and much more!</p>',
379
  'money-alt',
380
  '',
411
  </tr>
412
  <tr>
413
  <th scope="row">
414
+ <label for="cnb_cloud_enabled">Cloud
415
  <?php if ( $cnb_options['cloud_enabled'] == 0 ) { ?>
416
  <a href="<?php echo esc_url( ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page() ) ?>"
417
  class="cnb-nounderscore">
425
  <input id="cnb_cloud_enabled" class="cnb_toggle_checkbox" name="cnb[cloud_enabled]"
426
  type="checkbox"
427
  value="1" <?php checked( '1', $cnb_options['cloud_enabled'] ); ?> />
428
+ <label for="cnb_cloud_enabled" class="cnb_toggle_label">Enable Cloud</label>
429
  <span data-cnb_toggle_state_label="cnb_cloud_enabled"
430
  class="cnb_toggle_state cnb_toggle_false">(Inactive)</span>
431
  <span data-cnb_toggle_state_label="cnb_cloud_enabled"
432
  class="cnb_toggle_state cnb_toggle_true">Active</span>
433
  <?php if ( $cnb_options['cloud_enabled'] == 0 ) { ?>
434
+ <p class="description"><a href="<?php echo esc_url( ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page() ) ?>">Sign up</a> (free) to enable cloud and enjoy extra functionality.
435
  <a href="<?php echo esc_url( ( new CnbAdminFunctions() )->cnb_legacy_upgrade_page() ) ?>">Learn
436
  more</a>
437
  </p>
454
  $adminFunctions = new CnbAdminFunctions();
455
 
456
  wp_enqueue_script( CNB_SLUG . '-settings' );
457
+ wp_enqueue_script( CNB_SLUG . '-premium-activation' );
458
  wp_enqueue_script( CNB_SLUG . '-timezone-picker-fix' );
459
  wp_enqueue_script( CNB_SLUG . '-tally' );
460
 
477
 
478
  do_action( 'cnb_header' );
479
 
480
+ $cloud_successful = $status === 'cloud' && isset( $cnb_cloud_domain ) && ! ( $cnb_cloud_domain instanceof WP_Error ); ?>
 
 
 
 
 
 
 
 
 
 
481
 
482
  <div class="cnb-two-column-section">
483
  <div class="cnb-body-column">
src/autoload.php CHANGED
@@ -57,6 +57,9 @@ spl_autoload_register(
57
  'cnb\\admin\\domain\\cnbdomainviewupgradefinished' => '/admin/domain/partials/CnbDomainViewUpgradeFinished.php',
58
  'cnb\\admin\\domain\\cnbdomainviewupgradeinprogress' => '/admin/domain/partials/CnbDomainViewUpgradeInProgress.php',
59
  'cnb\\admin\\domain\\cnbdomainviewupgradeoverview' => '/admin/domain/partials/CnbDomainViewUpgradeOverview.php',
 
 
 
60
  'cnb\\admin\\legacy\\cnblegacycontroller' => '/admin/legacy/CnbLegacyController.php',
61
  'cnb\\admin\\legacy\\cnblegacyedit' => '/admin/legacy/CnbLegacyEdit.php',
62
  'cnb\\admin\\legacy\\cnblegacyupgrade' => '/admin/legacy/CnbLegacyUpgrade.php',
57
  'cnb\\admin\\domain\\cnbdomainviewupgradefinished' => '/admin/domain/partials/CnbDomainViewUpgradeFinished.php',
58
  'cnb\\admin\\domain\\cnbdomainviewupgradeinprogress' => '/admin/domain/partials/CnbDomainViewUpgradeInProgress.php',
59
  'cnb\\admin\\domain\\cnbdomainviewupgradeoverview' => '/admin/domain/partials/CnbDomainViewUpgradeOverview.php',
60
+ 'cnb\\admin\\gettingstarted\\gettingstartedcontroller' => '/admin/getting-started/class-getting-started-controller.php',
61
+ 'cnb\\admin\\gettingstarted\\gettingstartedrouter' => '/admin/getting-started/class-getting-started-router.php',
62
+ 'cnb\\admin\\gettingstarted\\gettingstartedview' => '/admin/getting-started/class-getting-started-view.php',
63
  'cnb\\admin\\legacy\\cnblegacycontroller' => '/admin/legacy/CnbLegacyController.php',
64
  'cnb\\admin\\legacy\\cnblegacyedit' => '/admin/legacy/CnbLegacyEdit.php',
65
  'cnb\\admin\\legacy\\cnblegacyupgrade' => '/admin/legacy/CnbLegacyUpgrade.php',
src/composer.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "require": {
3
- "php": "^7.2|^8.0",
4
- "sentry/sdk": "3.2.0"
5
- }
6
- }
 
 
 
 
 
 
src/composer.lock DELETED
@@ -1,1884 +0,0 @@
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/CnbUtils.php CHANGED
@@ -257,6 +257,11 @@ class CnbUtils {
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'] );
257
  return $default;
258
  }
259
 
260
+ /**
261
+ * Check if the error_reporting settings is set and enabled.
262
+ *
263
+ * @return bool
264
+ */
265
  function is_reporting_enabled() {
266
  $cnb_options = get_option( 'cnb' );
267
  return ( key_exists( 'error_reporting', $cnb_options ) && $cnb_options['error_reporting'] );
src/utils/class-cnb-sentry.php CHANGED
@@ -44,8 +44,9 @@ class Cnb_Sentry {
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'] ) {
@@ -70,7 +71,6 @@ class Cnb_Sentry {
70
  'environment' => WP_DEBUG ? 'development' : 'production',
71
  ] );
72
 
73
-
74
  self::setup_global_transaction($op_name);
75
  $cnb_sentry_init = true;
76
  }
@@ -86,17 +86,6 @@ class Cnb_Sentry {
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
  *
44
  }
45
 
46
  $cnb_options = get_option( 'cnb' );
47
+ // Only do with permission AND PHP > 7 (since that's what Sentry (/composer) requires)
48
+ // Verify this number is the same as used in src/vendor/composer/platform_check.php
49
+ if ( version_compare( PHP_VERSION, '7.2.5', '>=' )
50
  && ! class_exists( 'Sentry\SentrySdk' )
51
  && key_exists( 'error_reporting', $cnb_options )
52
  && $cnb_options['error_reporting'] ) {
71
  'environment' => WP_DEBUG ? 'development' : 'production',
72
  ] );
73
 
 
74
  self::setup_global_transaction($op_name);
75
  $cnb_sentry_init = true;
76
  }
86
  } );
87
  }
88
 
 
 
 
 
 
 
 
 
 
 
 
89
  /**
90
  * Finish the transaction, this submits the transaction and its span to Sentry
91
  *
src/vendor/composer/InstalledVersions.php CHANGED
@@ -28,7 +28,7 @@ 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
 
@@ -39,7 +39,7 @@ class InstalledVersions
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
 
@@ -243,7 +243,7 @@ class InstalledVersions
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
  {
@@ -257,7 +257,7 @@ class InstalledVersions
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
  {
@@ -280,7 +280,7 @@ class InstalledVersions
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
  {
@@ -303,7 +303,7 @@ class InstalledVersions
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
  {
@@ -313,7 +313,7 @@ class InstalledVersions
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
  {
28
  {
29
  /**
30
  * @var mixed[]|null
31
+ * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
32
  */
33
  private static $installed;
34
 
39
 
40
  /**
41
  * @var array[]
42
+ * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
43
  */
44
  private static $installedByVendor = array();
45
 
243
 
244
  /**
245
  * @return array
246
+ * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
247
  */
248
  public static function getRootPackage()
249
  {
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, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
261
  */
262
  public static function getRawData()
263
  {
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, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
284
  */
285
  public static function getAllRawData()
286
  {
303
  * @param array[] $data A vendor/composer/installed.php data set
304
  * @return void
305
  *
306
+ * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
307
  */
308
  public static function reload($data)
309
  {
313
 
314
  /**
315
  * @return array[]
316
+ * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
317
  */
318
  private static function getInstalled()
319
  {
src/vendor/composer/autoload_files.php CHANGED
@@ -7,9 +7,9 @@ $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',
7
 
8
  return array(
9
  'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
10
+ '9c67151ae59aff4788964ce8eb2a0f43' => $vendorDir . '/clue/stream-filter/src/functions_include.php',
11
  '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
12
  '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.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',
src/vendor/composer/autoload_psr4.php CHANGED
@@ -9,20 +9,16 @@ 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'),
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\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
 
13
  'Sentry\\' => array($vendorDir . '/sentry/sentry/src'),
14
  'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
15
+ 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
16
  'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
 
17
  'Jean85\\' => array($vendorDir . '/jean85/pretty-package-versions/src'),
18
  'Http\\Promise\\' => array($vendorDir . '/php-http/promise/src'),
19
+ 'Http\\Message\\' => array($vendorDir . '/php-http/message/src', $vendorDir . '/php-http/message-factory/src'),
 
20
  'Http\\Discovery\\' => array($vendorDir . '/php-http/discovery/src'),
21
+ 'Http\\Client\\Curl\\' => array($vendorDir . '/php-http/curl-client/src'),
22
  'Http\\Client\\Common\\' => array($vendorDir . '/php-http/client-common/src'),
23
  'Http\\Client\\' => array($vendorDir . '/php-http/httplug/src'),
24
  'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
src/vendor/composer/autoload_static.php CHANGED
@@ -8,9 +8,9 @@ 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',
@@ -24,10 +24,7 @@ class ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44
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' =>
@@ -35,7 +32,6 @@ class ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44
35
  'Psr\\Log\\' => 8,
36
  'Psr\\Http\\Message\\' => 17,
37
  'Psr\\Http\\Client\\' => 16,
38
- 'Psr\\Container\\' => 14,
39
  ),
40
  'J' =>
41
  array (
@@ -45,8 +41,8 @@ class ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44
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
  ),
@@ -74,22 +70,10 @@ class ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44
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',
@@ -100,17 +84,13 @@ class ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44
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',
@@ -121,17 +101,17 @@ class ComposerStaticInit447dd0acaa396d40f40a0cb37adffd44
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',
8
  {
9
  public static $files = array (
10
  'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
11
+ '9c67151ae59aff4788964ce8eb2a0f43' => __DIR__ . '/..' . '/clue/stream-filter/src/functions_include.php',
12
  '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
13
  '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.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',
24
  'Symfony\\Polyfill\\Uuid\\' => 22,
25
  'Symfony\\Polyfill\\Php80\\' => 23,
26
  'Symfony\\Polyfill\\Php73\\' => 23,
 
 
27
  'Symfony\\Component\\OptionsResolver\\' => 34,
 
28
  'Sentry\\' => 7,
29
  ),
30
  'P' =>
32
  'Psr\\Log\\' => 8,
33
  'Psr\\Http\\Message\\' => 17,
34
  'Psr\\Http\\Client\\' => 16,
 
35
  ),
36
  'J' =>
37
  array (
41
  array (
42
  'Http\\Promise\\' => 13,
43
  'Http\\Message\\' => 13,
 
44
  'Http\\Discovery\\' => 15,
45
+ 'Http\\Client\\Curl\\' => 17,
46
  'Http\\Client\\Common\\' => 19,
47
  'Http\\Client\\' => 12,
48
  ),
70
  array (
71
  0 => __DIR__ . '/..' . '/symfony/polyfill-php73',
72
  ),
 
 
 
 
 
 
 
 
73
  'Symfony\\Component\\OptionsResolver\\' =>
74
  array (
75
  0 => __DIR__ . '/..' . '/symfony/options-resolver',
76
  ),
 
 
 
 
77
  'Sentry\\' =>
78
  array (
79
  0 => __DIR__ . '/..' . '/sentry/sentry/src',
84
  ),
85
  'Psr\\Http\\Message\\' =>
86
  array (
87
+ 0 => __DIR__ . '/..' . '/psr/http-factory/src',
88
+ 1 => __DIR__ . '/..' . '/psr/http-message/src',
89
  ),
90
  'Psr\\Http\\Client\\' =>
91
  array (
92
  0 => __DIR__ . '/..' . '/psr/http-client/src',
93
  ),
 
 
 
 
94
  'Jean85\\' =>
95
  array (
96
  0 => __DIR__ . '/..' . '/jean85/pretty-package-versions/src',
101
  ),
102
  'Http\\Message\\' =>
103
  array (
104
+ 0 => __DIR__ . '/..' . '/php-http/message/src',
105
+ 1 => __DIR__ . '/..' . '/php-http/message-factory/src',
 
 
 
 
106
  ),
107
  'Http\\Discovery\\' =>
108
  array (
109
  0 => __DIR__ . '/..' . '/php-http/discovery/src',
110
  ),
111
+ 'Http\\Client\\Curl\\' =>
112
+ array (
113
+ 0 => __DIR__ . '/..' . '/php-http/curl-client/src',
114
+ ),
115
  'Http\\Client\\Common\\' =>
116
  array (
117
  0 => __DIR__ . '/..' . '/php-http/client-common/src',
src/vendor/composer/installed.json CHANGED
@@ -158,17 +158,17 @@
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": {
@@ -189,11 +189,11 @@
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",
@@ -256,7 +256,7 @@
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
  {
@@ -274,67 +274,6 @@
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",
@@ -475,19 +414,91 @@
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": {
@@ -505,7 +516,7 @@
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": {
@@ -541,7 +552,7 @@
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
  },
@@ -804,57 +815,6 @@
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",
@@ -1124,78 +1084,19 @@
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": {
@@ -1238,11 +1139,11 @@
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",
@@ -1277,7 +1178,7 @@
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
  {
@@ -1293,8 +1194,8 @@
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",
@@ -1343,7 +1244,7 @@
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
  {
@@ -1361,177 +1262,6 @@
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",
@@ -1856,92 +1586,6 @@
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,
158
  },
159
  {
160
  "name": "guzzlehttp/psr7",
161
+ "version": "2.4.0",
162
+ "version_normalized": "2.4.0.0",
163
  "source": {
164
  "type": "git",
165
  "url": "https://github.com/guzzle/psr7.git",
166
+ "reference": "13388f00956b1503577598873fffb5ae994b5737"
167
  },
168
  "dist": {
169
  "type": "zip",
170
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/13388f00956b1503577598873fffb5ae994b5737",
171
+ "reference": "13388f00956b1503577598873fffb5ae994b5737",
172
  "shasum": ""
173
  },
174
  "require": {
189
  "suggest": {
190
  "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
191
  },
192
+ "time": "2022-06-20T21:43:11+00:00",
193
  "type": "library",
194
  "extra": {
195
  "branch-alias": {
196
+ "dev-master": "2.4-dev"
197
  }
198
  },
199
  "installation-source": "dist",
256
  ],
257
  "support": {
258
  "issues": "https://github.com/guzzle/psr7/issues",
259
+ "source": "https://github.com/guzzle/psr7/tree/2.4.0"
260
  },
261
  "funding": [
262
  {
274
  ],
275
  "install-path": "../guzzlehttp/psr7"
276
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  {
278
  "name": "jean85/pretty-package-versions",
279
  "version": "2.0.5",
414
  },
415
  "install-path": "../php-http/client-common"
416
  },
417
+ {
418
+ "name": "php-http/curl-client",
419
+ "version": "2.2.1",
420
+ "version_normalized": "2.2.1.0",
421
+ "source": {
422
+ "type": "git",
423
+ "url": "https://github.com/php-http/curl-client.git",
424
+ "reference": "2ed4245a817d859dd0c1d51c7078cdb343cf5233"
425
+ },
426
+ "dist": {
427
+ "type": "zip",
428
+ "url": "https://api.github.com/repos/php-http/curl-client/zipball/2ed4245a817d859dd0c1d51c7078cdb343cf5233",
429
+ "reference": "2ed4245a817d859dd0c1d51c7078cdb343cf5233",
430
+ "shasum": ""
431
+ },
432
+ "require": {
433
+ "ext-curl": "*",
434
+ "php": "^7.1 || ^8.0",
435
+ "php-http/discovery": "^1.6",
436
+ "php-http/httplug": "^2.0",
437
+ "php-http/message": "^1.2",
438
+ "psr/http-client": "^1.0",
439
+ "psr/http-factory": "^1.0",
440
+ "symfony/options-resolver": "^3.4 || ^4.0 || ^5.0 || ^6.0"
441
+ },
442
+ "provide": {
443
+ "php-http/async-client-implementation": "1.0",
444
+ "php-http/client-implementation": "1.0",
445
+ "psr/http-client-implementation": "1.0"
446
+ },
447
+ "require-dev": {
448
+ "guzzlehttp/psr7": "^1.0",
449
+ "laminas/laminas-diactoros": "^2.0",
450
+ "php-http/client-integration-tests": "^3.0",
451
+ "phpunit/phpunit": "^7.5 || ^9.4"
452
+ },
453
+ "time": "2021-12-10T18:02:07+00:00",
454
+ "type": "library",
455
+ "extra": {
456
+ "branch-alias": {
457
+ "dev-master": "2.x-dev"
458
+ }
459
+ },
460
+ "installation-source": "dist",
461
+ "autoload": {
462
+ "psr-4": {
463
+ "Http\\Client\\Curl\\": "src/"
464
+ }
465
+ },
466
+ "notification-url": "https://packagist.org/downloads/",
467
+ "license": [
468
+ "MIT"
469
+ ],
470
+ "authors": [
471
+ {
472
+ "name": "Михаил Красильников",
473
+ "email": "m.krasilnikov@yandex.ru"
474
+ }
475
+ ],
476
+ "description": "PSR-18 and HTTPlug Async client with cURL",
477
+ "homepage": "http://php-http.org",
478
+ "keywords": [
479
+ "curl",
480
+ "http",
481
+ "psr-18"
482
+ ],
483
+ "support": {
484
+ "issues": "https://github.com/php-http/curl-client/issues",
485
+ "source": "https://github.com/php-http/curl-client/tree/2.2.1"
486
+ },
487
+ "install-path": "../php-http/curl-client"
488
+ },
489
  {
490
  "name": "php-http/discovery",
491
+ "version": "1.14.3",
492
+ "version_normalized": "1.14.3.0",
493
  "source": {
494
  "type": "git",
495
  "url": "https://github.com/php-http/discovery.git",
496
+ "reference": "31d8ee46d0215108df16a8527c7438e96a4d7735"
497
  },
498
  "dist": {
499
  "type": "zip",
500
+ "url": "https://api.github.com/repos/php-http/discovery/zipball/31d8ee46d0215108df16a8527c7438e96a4d7735",
501
+ "reference": "31d8ee46d0215108df16a8527c7438e96a4d7735",
502
  "shasum": ""
503
  },
504
  "require": {
516
  "suggest": {
517
  "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories"
518
  },
519
+ "time": "2022-07-11T14:04:40+00:00",
520
  "type": "library",
521
  "extra": {
522
  "branch-alias": {
552
  ],
553
  "support": {
554
  "issues": "https://github.com/php-http/discovery/issues",
555
+ "source": "https://github.com/php-http/discovery/tree/1.14.3"
556
  },
557
  "install-path": "../php-http/discovery"
558
  },
815
  },
816
  "install-path": "../php-http/promise"
817
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
818
  {
819
  "name": "psr/http-client",
820
  "version": "1.0.1",
1084
  },
1085
  "install-path": "../ralouphie/getallheaders"
1086
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1087
  {
1088
  "name": "sentry/sentry",
1089
+ "version": "3.7.0",
1090
+ "version_normalized": "3.7.0.0",
1091
  "source": {
1092
  "type": "git",
1093
  "url": "https://github.com/getsentry/sentry-php.git",
1094
+ "reference": "877bca3f0f0ac0fc8ec0a218c6070cccea266795"
1095
  },
1096
  "dist": {
1097
  "type": "zip",
1098
+ "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/877bca3f0f0ac0fc8ec0a218c6070cccea266795",
1099
+ "reference": "877bca3f0f0ac0fc8ec0a218c6070cccea266795",
1100
  "shasum": ""
1101
  },
1102
  "require": {
1139
  "suggest": {
1140
  "monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler."
1141
  },
1142
+ "time": "2022-07-18T07:55:36+00:00",
1143
  "type": "library",
1144
  "extra": {
1145
  "branch-alias": {
1146
+ "dev-master": "3.7.x-dev"
1147
  }
1148
  },
1149
  "installation-source": "dist",
1178
  ],
1179
  "support": {
1180
  "issues": "https://github.com/getsentry/sentry-php/issues",
1181
+ "source": "https://github.com/getsentry/sentry-php/tree/3.7.0"
1182
  },
1183
  "funding": [
1184
  {
1194
  },
1195
  {
1196
  "name": "symfony/deprecation-contracts",
1197
+ "version": "v2.5.2",
1198
+ "version_normalized": "2.5.2.0",
1199
  "source": {
1200
  "type": "git",
1201
  "url": "https://github.com/symfony/deprecation-contracts.git",
1244
  "description": "A generic function and convention to trigger deprecation notices",
1245
  "homepage": "https://symfony.com",
1246
  "support": {
1247
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
1248
  },
1249
  "funding": [
1250
  {
1262
  ],
1263
  "install-path": "../symfony/deprecation-contracts"
1264
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1265
  {
1266
  "name": "symfony/options-resolver",
1267
  "version": "v5.4.3",
1586
  }
1587
  ],
1588
  "install-path": "../symfony/polyfill-uuid"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1589
  }
1590
  ],
1591
  "dev": true,
src/vendor/composer/installed.php CHANGED
@@ -1,124 +1,124 @@
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(
@@ -130,28 +130,19 @@
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(
@@ -163,26 +154,25 @@
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(
@@ -194,115 +184,73 @@
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
  ),
1
  <?php return array(
2
  'root' => array(
3
+ 'name' => '__root__',
4
  'pretty_version' => 'dev-master',
5
  'version' => 'dev-master',
6
+ 'reference' => 'd2c6b667fba2a45bba8a3ce6ced006dfa882dfbd',
7
  'type' => 'library',
8
  'install_path' => __DIR__ . '/../../',
9
  'aliases' => array(),
 
 
10
  'dev' => true,
11
  ),
12
  'versions' => array(
13
  '__root__' => array(
14
  'pretty_version' => 'dev-master',
15
  'version' => 'dev-master',
16
+ 'reference' => 'd2c6b667fba2a45bba8a3ce6ced006dfa882dfbd',
17
  'type' => 'library',
18
  'install_path' => __DIR__ . '/../../',
19
  'aliases' => array(),
 
20
  'dev_requirement' => false,
21
  ),
22
  'clue/stream-filter' => array(
23
  'pretty_version' => 'v1.6.0',
24
  'version' => '1.6.0.0',
25
+ 'reference' => 'd6169430c7731d8509da7aecd0af756a5747b78e',
26
  'type' => 'library',
27
  'install_path' => __DIR__ . '/../clue/stream-filter',
28
  'aliases' => array(),
 
29
  'dev_requirement' => false,
30
  ),
31
  'guzzlehttp/promises' => array(
32
  'pretty_version' => '1.5.1',
33
  'version' => '1.5.1.0',
34
+ 'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da',
35
  'type' => 'library',
36
  'install_path' => __DIR__ . '/../guzzlehttp/promises',
37
  'aliases' => array(),
 
38
  'dev_requirement' => false,
39
  ),
40
  'guzzlehttp/psr7' => array(
41
+ 'pretty_version' => '2.4.0',
42
+ 'version' => '2.4.0.0',
43
+ 'reference' => '13388f00956b1503577598873fffb5ae994b5737',
44
  'type' => 'library',
45
  'install_path' => __DIR__ . '/../guzzlehttp/psr7',
46
  'aliases' => array(),
 
 
 
 
 
 
 
 
 
 
47
  'dev_requirement' => false,
48
  ),
49
  'jean85/pretty-package-versions' => array(
50
  'pretty_version' => '2.0.5',
51
  'version' => '2.0.5.0',
52
+ 'reference' => 'ae547e455a3d8babd07b96966b17d7fd21d9c6af',
53
  'type' => 'library',
54
  'install_path' => __DIR__ . '/../jean85/pretty-package-versions',
55
  'aliases' => array(),
 
56
  'dev_requirement' => false,
57
  ),
58
  'php-http/async-client-implementation' => array(
59
  'dev_requirement' => false,
60
  'provided' => array(
61
+ 0 => '1.0',
62
  ),
63
  ),
64
  'php-http/client-common' => array(
65
  'pretty_version' => '2.5.0',
66
  'version' => '2.5.0.0',
67
+ 'reference' => 'd135751167d57e27c74de674d6a30cef2dc8e054',
68
  'type' => 'library',
69
  'install_path' => __DIR__ . '/../php-http/client-common',
70
  'aliases' => array(),
 
71
  'dev_requirement' => false,
72
  ),
73
  'php-http/client-implementation' => array(
74
  'dev_requirement' => false,
75
  'provided' => array(
76
+ 0 => '1.0',
77
  ),
78
  ),
79
+ 'php-http/curl-client' => array(
80
+ 'pretty_version' => '2.2.1',
81
+ 'version' => '2.2.1.0',
82
+ 'reference' => '2ed4245a817d859dd0c1d51c7078cdb343cf5233',
83
+ 'type' => 'library',
84
+ 'install_path' => __DIR__ . '/../php-http/curl-client',
85
+ 'aliases' => array(),
86
+ 'dev_requirement' => false,
87
+ ),
88
  'php-http/discovery' => array(
89
+ 'pretty_version' => '1.14.3',
90
+ 'version' => '1.14.3.0',
91
+ 'reference' => '31d8ee46d0215108df16a8527c7438e96a4d7735',
92
  'type' => 'library',
93
  'install_path' => __DIR__ . '/../php-http/discovery',
94
  'aliases' => array(),
 
95
  'dev_requirement' => false,
96
  ),
97
  'php-http/httplug' => array(
98
  'pretty_version' => '2.3.0',
99
  'version' => '2.3.0.0',
100
+ 'reference' => 'f640739f80dfa1152533976e3c112477f69274eb',
101
  'type' => 'library',
102
  'install_path' => __DIR__ . '/../php-http/httplug',
103
  'aliases' => array(),
 
104
  'dev_requirement' => false,
105
  ),
106
  'php-http/message' => array(
107
  'pretty_version' => '1.13.0',
108
  'version' => '1.13.0.0',
109
+ 'reference' => '7886e647a30a966a1a8d1dad1845b71ca8678361',
110
  'type' => 'library',
111
  'install_path' => __DIR__ . '/../php-http/message',
112
  'aliases' => array(),
 
113
  'dev_requirement' => false,
114
  ),
115
  'php-http/message-factory' => array(
116
  'pretty_version' => 'v1.0.2',
117
  'version' => '1.0.2.0',
118
+ 'reference' => 'a478cb11f66a6ac48d8954216cfed9aa06a501a1',
119
  'type' => 'library',
120
  'install_path' => __DIR__ . '/../php-http/message-factory',
121
  'aliases' => array(),
 
122
  'dev_requirement' => false,
123
  ),
124
  'php-http/message-factory-implementation' => array(
130
  'php-http/promise' => array(
131
  'pretty_version' => '1.1.0',
132
  'version' => '1.1.0.0',
 
 
 
133
  'reference' => '4c4c1f9b7289a2ec57cde7f1e9762a5789506f88',
 
 
 
 
 
134
  'type' => 'library',
135
+ 'install_path' => __DIR__ . '/../php-http/promise',
136
  'aliases' => array(),
 
137
  'dev_requirement' => false,
138
  ),
139
  'psr/http-client' => array(
140
  'pretty_version' => '1.0.1',
141
  'version' => '1.0.1.0',
142
+ 'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
143
  'type' => 'library',
144
  'install_path' => __DIR__ . '/../psr/http-client',
145
  'aliases' => array(),
 
146
  'dev_requirement' => false,
147
  ),
148
  'psr/http-client-implementation' => array(
154
  'psr/http-factory' => array(
155
  'pretty_version' => '1.0.1',
156
  'version' => '1.0.1.0',
157
+ 'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be',
158
  'type' => 'library',
159
  'install_path' => __DIR__ . '/../psr/http-factory',
160
  'aliases' => array(),
 
161
  'dev_requirement' => false,
162
  ),
163
  'psr/http-factory-implementation' => array(
164
  'dev_requirement' => false,
165
  'provided' => array(
166
  0 => '1.0',
 
167
  ),
168
  ),
169
  'psr/http-message' => array(
170
  'pretty_version' => '1.0.1',
171
  'version' => '1.0.1.0',
172
+ 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
173
  'type' => 'library',
174
  'install_path' => __DIR__ . '/../psr/http-message',
175
  'aliases' => array(),
 
176
  'dev_requirement' => false,
177
  ),
178
  'psr/http-message-implementation' => array(
184
  'psr/log' => array(
185
  'pretty_version' => '1.1.4',
186
  'version' => '1.1.4.0',
187
+ 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
188
  'type' => 'library',
189
  'install_path' => __DIR__ . '/../psr/log',
190
  'aliases' => array(),
 
191
  'dev_requirement' => false,
192
  ),
193
  'ralouphie/getallheaders' => array(
194
  'pretty_version' => '3.0.3',
195
  'version' => '3.0.3.0',
196
+ 'reference' => '120b605dfeb996808c31b6477290a714d356e822',
197
  'type' => 'library',
198
  'install_path' => __DIR__ . '/../ralouphie/getallheaders',
199
  'aliases' => array(),
 
 
 
 
 
 
 
 
 
 
200
  'dev_requirement' => false,
201
  ),
202
  'sentry/sentry' => array(
203
+ 'pretty_version' => '3.7.0',
204
+ 'version' => '3.7.0.0',
205
+ 'reference' => '877bca3f0f0ac0fc8ec0a218c6070cccea266795',
206
  'type' => 'library',
207
  'install_path' => __DIR__ . '/../sentry/sentry',
208
  'aliases' => array(),
 
209
  'dev_requirement' => false,
210
  ),
211
  'symfony/deprecation-contracts' => array(
212
+ 'pretty_version' => 'v2.5.2',
213
+ 'version' => '2.5.2.0',
 
 
 
214
  'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  'type' => 'library',
216
+ 'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
217
  'aliases' => array(),
 
218
  'dev_requirement' => false,
219
  ),
 
 
 
 
 
 
220
  'symfony/options-resolver' => array(
221
  'pretty_version' => 'v5.4.3',
222
  'version' => '5.4.3.0',
223
+ 'reference' => 'cc1147cb11af1b43f503ac18f31aa3bec213aba8',
224
  'type' => 'library',
225
  'install_path' => __DIR__ . '/../symfony/options-resolver',
226
  'aliases' => array(),
 
227
  'dev_requirement' => false,
228
  ),
229
  'symfony/polyfill-php73' => array(
230
  'pretty_version' => 'v1.26.0',
231
  'version' => '1.26.0.0',
232
+ 'reference' => 'e440d35fa0286f77fb45b79a03fedbeda9307e85',
233
  'type' => 'library',
234
  'install_path' => __DIR__ . '/../symfony/polyfill-php73',
235
  'aliases' => array(),
 
236
  'dev_requirement' => false,
237
  ),
238
  'symfony/polyfill-php80' => array(
239
  'pretty_version' => 'v1.26.0',
240
  'version' => '1.26.0.0',
241
+ 'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
242
  'type' => 'library',
243
  'install_path' => __DIR__ . '/../symfony/polyfill-php80',
244
  'aliases' => array(),
 
245
  'dev_requirement' => false,
246
  ),
247
  'symfony/polyfill-uuid' => array(
248
  'pretty_version' => 'v1.26.0',
249
  'version' => '1.26.0.0',
 
 
 
250
  'reference' => 'a41886c1c81dc075a09c71fe6db5b9d68c79de23',
 
 
 
 
 
251
  'type' => 'library',
252
+ 'install_path' => __DIR__ . '/../symfony/polyfill-uuid',
253
  'aliases' => array(),
 
254
  'dev_requirement' => false,
255
  ),
256
  ),
src/vendor/composer/platform_check.php CHANGED
@@ -4,8 +4,8 @@
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) {
4
 
5
  $issues = array();
6
 
7
+ if (!(PHP_VERSION_ID >= 70205)) {
8
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.5". You are running ' . PHP_VERSION . '.';
9
  }
10
 
11
  if ($issues) {
src/vendor/guzzlehttp/psr7/CHANGELOG.md CHANGED
@@ -7,20 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
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
 
7
 
8
  ## Unreleased
9
 
10
+ ## 2.4.0 - 2022-06-20
11
+
12
+ ### Added
13
+
14
+ - Added provisional PHP 8.2 support
15
+ - Added `UriComparator::isCrossOrigin` method
16
+
17
  ## 2.3.0 - 2022-06-09
18
 
19
  ### Fixed
20
 
21
+ - Added `Header::splitList` method
22
+ - Added `Utils::tryGetContents` method
23
+ - Improved `Stream::getContents` method
24
  - Updated mimetype mappings
25
 
26
  ## 2.2.2 - 2022-06-08
27
 
28
  ### Fixed
29
 
30
+ - Fix `Message::parseRequestUri` for numeric headers
31
  - Re-wrap exceptions thrown in `fread` into runtime exceptions
32
  - Throw an exception when multipart options is misformatted
33
 
src/vendor/guzzlehttp/psr7/README.md CHANGED
@@ -1,6 +1,6 @@
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
 
@@ -669,7 +669,7 @@ manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__to
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`
@@ -694,6 +694,16 @@ associative array of key => value.
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
@@ -818,21 +828,25 @@ Whether two URIs can be considered equivalent. Both URIs are normalized automati
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
1
  # PSR-7 Message Implementation
2
 
3
+ This repository contains a full [PSR-7](https://www.php-fig.org/psr/psr-7/)
4
  message implementation, several stream decorators, and some helpful
5
  functionality like query string parsing.
6
 
669
 
670
  `public static function fromParts(array $parts): UriInterface`
671
 
672
+ Creates a URI from a hash of [`parse_url`](https://www.php.net/manual/en/function.parse-url.php) components.
673
 
674
 
675
  ### `GuzzleHttp\Psr7\Uri::withQueryValue`
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
+ ## Cross-Origin Detection
698
+
699
+ `GuzzleHttp\Psr7\UriComparator` provides methods to determine if a modified URL should be considered cross-origin.
700
+
701
+ ### `GuzzleHttp\Psr7\UriComparator::isCrossOrigin`
702
+
703
+ `public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool`
704
+
705
+ Determines if a modified URL should be considered cross-origin with respect to an original URL.
706
+
707
  ## Reference Resolution
708
 
709
  `GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
828
  This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
829
  equivalence or difference of relative references does not mean anything.
830
 
831
+
832
  ## Version Guidance
833
 
834
  | Version | Status | PHP Version |
835
  |---------|----------------|------------------|
836
+ | 1.x | Security fixes | >=5.4,<8.1 |
837
  | 2.x | Latest | ^7.2.5 \|\| ^8.0 |
838
 
839
+
840
  ## Security
841
 
842
  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.
843
 
844
+
845
  ## License
846
 
847
  Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
848
 
849
+
850
  ## For Enterprise
851
 
852
  Available as part of the Tidelift Subscription
src/vendor/guzzlehttp/psr7/composer.json CHANGED
@@ -79,7 +79,7 @@
79
  },
80
  "extra": {
81
  "branch-alias": {
82
- "dev-master": "2.3-dev"
83
  }
84
  },
85
  "config": {
79
  },
80
  "extra": {
81
  "branch-alias": {
82
+ "dev-master": "2.4-dev"
83
  }
84
  },
85
  "config": {
src/vendor/guzzlehttp/psr7/src/CachingStream.php CHANGED
@@ -20,6 +20,11 @@ final class CachingStream implements StreamInterface
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
  *
20
  /** @var int Number of bytes to skip reading due to a write on the buffer */
21
  private $skipReadBytes = 0;
22
 
23
+ /**
24
+ * @var StreamInterface
25
+ */
26
+ private $stream;
27
+
28
  /**
29
  * We will treat the buffer object as the body of the stream
30
  *
src/vendor/guzzlehttp/psr7/src/DroppingStream.php CHANGED
@@ -17,6 +17,9 @@ final class DroppingStream implements StreamInterface
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.
17
  /** @var int */
18
  private $maxLength;
19
 
20
+ /** @var StreamInterface */
21
+ private $stream;
22
+
23
  /**
24
  * @param StreamInterface $stream Underlying stream to decorate.
25
  * @param int $maxLength Maximum size before dropping data.
src/vendor/guzzlehttp/psr7/src/FnStream.php CHANGED
@@ -12,6 +12,7 @@ use Psr\Http\Message\StreamInterface;
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 = [
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
+ #[\AllowDynamicProperties]
16
  final class FnStream implements StreamInterface
17
  {
18
  private const SLOTS = [
src/vendor/guzzlehttp/psr7/src/InflateStream.php CHANGED
@@ -21,6 +21,9 @@ final class InflateStream implements StreamInterface
21
  {
22
  use StreamDecoratorTrait;
23
 
 
 
 
24
  public function __construct(StreamInterface $stream)
25
  {
26
  $resource = StreamWrapper::getResource($stream);
21
  {
22
  use StreamDecoratorTrait;
23
 
24
+ /** @var StreamInterface */
25
+ private $stream;
26
+
27
  public function __construct(StreamInterface $stream)
28
  {
29
  $resource = StreamWrapper::getResource($stream);
src/vendor/guzzlehttp/psr7/src/LazyOpenStream.php CHANGED
@@ -10,6 +10,7 @@ use Psr\Http\Message\StreamInterface;
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;
10
  * Lazily reads or writes to a file that is opened only after an IO operation
11
  * take place on the stream.
12
  */
13
+ #[\AllowDynamicProperties]
14
  final class LazyOpenStream implements StreamInterface
15
  {
16
  use StreamDecoratorTrait;
src/vendor/guzzlehttp/psr7/src/LimitStream.php CHANGED
@@ -19,6 +19,9 @@ final class LimitStream implements StreamInterface
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
19
  /** @var int Limit the number of bytes that can be read */
20
  private $limit;
21
 
22
+ /** @var StreamInterface */
23
+ private $stream;
24
+
25
  /**
26
  * @param StreamInterface $stream Stream to wrap
27
  * @param int $limit Total number of bytes to allow to be read
src/vendor/guzzlehttp/psr7/src/MultipartStream.php CHANGED
@@ -17,6 +17,9 @@ final class MultipartStream implements StreamInterface
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,
17
  /** @var string */
18
  private $boundary;
19
 
20
+ /** @var StreamInterface */
21
+ private $stream;
22
+
23
  /**
24
  * @param array $elements Array of associative arrays, each containing a
25
  * required "name" key mapping to the form field,
src/vendor/guzzlehttp/psr7/src/NoSeekStream.php CHANGED
@@ -13,6 +13,9 @@ 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');
13
  {
14
  use StreamDecoratorTrait;
15
 
16
+ /** @var StreamInterface */
17
+ private $stream;
18
+
19
  public function seek($offset, $whence = SEEK_SET): void
20
  {
21
  throw new \RuntimeException('Cannot seek a NoSeekStream');
src/vendor/guzzlehttp/psr7/src/UriComparator.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 determine if a modified URL should be considered cross-origin.
11
+ *
12
+ * @author Graham Campbell
13
+ */
14
+ final class UriComparator
15
+ {
16
+ /**
17
+ * Determines if a modified URL should be considered cross-origin with
18
+ * respect to an original URL.
19
+ */
20
+ public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool
21
+ {
22
+ if (\strcasecmp($original->getHost(), $modified->getHost()) !== 0) {
23
+ return true;
24
+ }
25
+
26
+ if ($original->getScheme() !== $modified->getScheme()) {
27
+ return true;
28
+ }
29
+
30
+ if (self::computePort($original) !== self::computePort($modified)) {
31
+ return true;
32
+ }
33
+
34
+ return false;
35
+ }
36
+
37
+ private static function computePort(UriInterface $uri): int
38
+ {
39
+ $port = $uri->getPort();
40
+
41
+ if (null !== $port) {
42
+ return $port;
43
+ }
44
+
45
+ return 'https' === $uri->getScheme() ? 443 : 80;
46
+ }
47
+
48
+ private function __construct()
49
+ {
50
+ // cannot be instantiated
51
+ }
52
+ }
src/vendor/http-interop/http-factory-guzzle/.github/workflows/ci.yaml DELETED
@@ -1,60 +0,0 @@
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 DELETED
@@ -1,21 +0,0 @@
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 DELETED
@@ -1,7 +0,0 @@
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 DELETED
@@ -1,37 +0,0 @@
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 DELETED
@@ -1,15 +0,0 @@
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 DELETED
@@ -1,15 +0,0 @@
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 DELETED
@@ -1,24 +0,0 @@
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 DELETED
@@ -1,26 +0,0 @@
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 DELETED
@@ -1,25 +0,0 @@
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 DELETED
@@ -1,15 +0,0 @@
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/php-http/curl-client/.github/workflows/Build-Test.yml ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Tests
2
+
3
+ # Run this workflow every time a new commit pushed to your repository
4
+ on:
5
+ push:
6
+
7
+ jobs:
8
+ tests:
9
+ if: "! contains(toJSON(github.event.commits.*.msg), 'skip') && ! contains(toJSON(github.event.commits.*.msg), 'ci')" #skip ci...
10
+ runs-on: ${{ matrix.operating-system }}
11
+
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ operating-system: [ubuntu-20.04]
16
+ php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1']
17
+ include:
18
+ - operating-system: ubuntu-16.04
19
+ php-versions: '7.1'
20
+ COMPOSER_FLAGS: '--prefer-stable --prefer-lowest'
21
+ PHPUNIT_FLAGS: '--coverage-clover build/coverage.xml'
22
+
23
+ name: PHP ${{ matrix.php-versions }} - ${{ matrix.operating-system }}
24
+
25
+ env:
26
+ extensions: curl json libxml dom
27
+ key: cache-v1 # can be any string, change to clear the extension cache.
28
+
29
+ steps:
30
+ # Checks out a copy of your repository on the ubuntu machine
31
+ - name: Checkout code
32
+ uses: actions/checkout@v2
33
+
34
+ - name: Setup cache environment
35
+ id: extcache
36
+ uses: shivammathur/cache-extensions@v1
37
+ with:
38
+ php-version: ${{ matrix.php-versions }}
39
+ extensions: ${{ env.extensions }}
40
+ key: ${{ env.key }}
41
+
42
+ - name: Cache PHP Extensions
43
+ uses: actions/cache@v2
44
+ with:
45
+ path: ${{ steps.extcache.outputs.dir }}
46
+ key: ${{ steps.extcache.outputs.key }}
47
+ restore-keys: ${{ steps.extcache.outputs.key }}
48
+
49
+ - name: Cache Composer Dependencies
50
+ uses: actions/cache@v1
51
+ with:
52
+ path: ~/.composer/cache/files
53
+ key: dependencies-composer-${{ hashFiles('composer.json') }}
54
+
55
+ - name: Setup PHP Action
56
+ uses: shivammathur/setup-php@2.8.0
57
+ with:
58
+ php-version: ${{ matrix.php-versions }}
59
+ extensions: ${{ env.extensions }}
60
+ coverage: xdebug
61
+ tools: pecl, composer
62
+
63
+ - name: Get composer cache directory
64
+ id: composer-cache
65
+ run: echo "::set-output name=dir::$(composer config cache-files-dir)"
66
+
67
+ - name: Cache dependencies
68
+ uses: actions/cache@v2
69
+ with:
70
+ path: ${{ steps.composer-cache.outputs.dir }}
71
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
72
+ restore-keys: ${{ runner.os }}-composer-
73
+
74
+ - name: Install Composer dependencies
75
+ run: composer update ${{ matrix.COMPOSER_FLAGS }} --no-interaction
76
+
77
+ - name: boot test server
78
+ run: vendor/bin/http_test_server > /dev/null 2>&1 &
79
+
80
+ - name: Run tests
81
+ run: composer test
src/vendor/php-http/curl-client/.php_cs ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ return Symfony\CS\Config\Config::create()
4
+ ->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
5
+ ->fixers([])
6
+ ->finder(
7
+ Symfony\CS\Finder\DefaultFinder::create()->in(__DIR__ . '/src')
8
+ )
9
+ ;
src/vendor/php-http/curl-client/CHANGELOG.md ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ ## 2.2.1 - 2021-12-10
9
+
10
+ ### Added
11
+
12
+ - Symfony 6 support
13
+ - Tested with PHP 8.1
14
+
15
+ ## 2.2.0 - 2020-12-14
16
+
17
+ ### Added
18
+
19
+ - PHP 8.0 support
20
+
21
+ ## 2.1.0 - 2019-12-27
22
+
23
+ ### Added
24
+
25
+ - Symfony 5 support
26
+
27
+ ## 2.0.0 - 2019-03-05
28
+
29
+ ### Removed
30
+
31
+ - HHVM support removed.
32
+
33
+ ### Changed
34
+
35
+ - Minimal PHP version changed to 7.1.
36
+ - `Client::__construct` now expects PSR-17 factories instead of HTTPlug ones.
37
+
38
+ ### Added
39
+
40
+ - #41: Support [PSR-17](https://www.php-fig.org/psr/psr-17/) and
41
+ [PSR-18](https://www.php-fig.org/psr/psr-18/).
42
+
43
+
44
+ ## 1.7.1 - 2018-03-26
45
+
46
+ ### Fixed
47
+
48
+ - #36: Failure evaluating code: is_resource($handle) (string assertions are deprecated in PHP 7.2)
49
+
50
+
51
+ ## 1.7 - 2017-02-09
52
+
53
+ ### Changed
54
+
55
+ - #30: Make sure we rewind streams
56
+
57
+ ## 1.6.2 - 2017-01-02
58
+
59
+ ### Fixed
60
+
61
+ - #29: Request not using CURLOPT_POSTFIELDS have content-length set to
62
+
63
+ ### Changed
64
+
65
+ - Use binary mode to create response body stream.
66
+
67
+
68
+ ## 1.6.1 - 2016-11-11
69
+
70
+ ### Fixed
71
+
72
+ - #27: ErrorPlugin and sendAsyncRequest() incompatibility
73
+
74
+
75
+ ## 1.6 - 2016-09-12
76
+
77
+ ### Changed
78
+
79
+ - `Client::sendRequest` now throws `Http\Client\Exception\NetworkException` on network errors.
80
+ - `\UnexpectedValueException` replaced with `Http\Client\Exception\RequestException` in
81
+ `Client::sendRequest` and `Client::sendAsyncRequest`
82
+
83
+
84
+ ## 1.5.1 - 2016-08-29
85
+
86
+ ### Fixed
87
+
88
+ - #26: Combining CurlClient with StopwatchPlugin causes Promise onRejected handler to never be
89
+ invoked.
90
+
91
+
92
+ ## 1.5 - 2016-08-03
93
+
94
+ ### Changed
95
+
96
+ - Request body can be send with any method except GET, HEAD and TRACE.
97
+ - #25: Make discovery a hard dependency.
98
+
99
+
100
+ ## 1.4.2 - 2016-06-14
101
+
102
+ ### Added
103
+
104
+ - #23: "php-http/async-client-implementation" added to "provide" section.
105
+
106
+
107
+ ## 1.4.1 - 2016-05-30
108
+
109
+ ### Fixed
110
+
111
+ - #22: Cannot create the client using `HttpClientDiscovery`.
112
+
113
+
114
+ ## 1.4 - 2016-03-30
115
+
116
+ ### Changed
117
+
118
+ - #20: Minimize memory usage when reading large response body.
119
+
120
+
121
+ ## 1.3 - 2016-03-14
122
+
123
+ ### Fixed
124
+
125
+ - #18: Invalid "Expect" header.
126
+
127
+ ### Removed
128
+
129
+ - #13: Remove HeaderParser.
130
+
131
+
132
+ ## 1.2 - 2016-03-09
133
+
134
+ ### Added
135
+
136
+ - #16: Make sure discovery can find the curl client
137
+
138
+ ### Fixed
139
+
140
+ - #15: "Out of memory" sending large files.
141
+
142
+
143
+ ## 1.1.0 - 2016-01-29
144
+
145
+ ### Changed
146
+
147
+ - Switch to php-http/message 1.0.
148
+
149
+
150
+ ## 1.0.0 - 2016-01-28
151
+
152
+ First stable release.
153
+
154
+
155
+ ## 0.7.0 - 2016-01-26
156
+
157
+ ### Changed
158
+
159
+ - Migrate from `php-http/discovery` and `php-http/utils` to `php-http/message`.
160
+
161
+ ## 0.6.0 - 2016-01-12
162
+
163
+ ### Changed
164
+
165
+ - Root namespace changed from `Http\Curl` to `Http\Client\Curl`.
166
+ - Main client class name renamed from `CurlHttpClient` to `Client`.
167
+ - Minimum required [php-http/discovery](https://packagist.org/packages/php-http/discovery)
168
+ version changed to 0.5.
169
+
170
+
171
+ ## 0.5.0 - 2015-12-18
172
+
173
+ ### Changed
174
+
175
+ - Compatibility with php-http/httplug 1.0 beta
176
+ - Switch to php-http/discovery 0.4
177
+
178
+
179
+ ## 0.4.0 - 2015-12-16
180
+
181
+ ### Changed
182
+
183
+ - Switch to php-http/message-factory 1.0
184
+
185
+
186
+ ## 0.3.1 - 2015-12-14
187
+
188
+ ### Changed
189
+
190
+ - Requirements fixed.
191
+
192
+
193
+ ## 0.3.0 - 2015-11-24
194
+
195
+ ### Changed
196
+
197
+ - Use cURL constants as options keys.
198
+
199
+
200
+ ## 0.2.0 - 2015-11-17
201
+
202
+ ### Added
203
+
204
+ - HttpAsyncClient support.
205
+
206
+
207
+ ## 0.1.0 - 2015-11-11
208
+
209
+ ### Added
210
+
211
+ - Initial release
src/vendor/{symfony/service-contracts → php-http/curl-client}/LICENSE RENAMED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2018-2022 Fabien Potencier
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
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
src/vendor/php-http/curl-client/README.md ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Curl client for PHP HTTP
2
+
3
+ [![Latest Version](https://img.shields.io/github/release/php-http/curl-client.svg?style=flat-square)](https://github.com/php-http/curl-client/releases)
4
+ [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
5
+ [![Build Status](https://img.shields.io/github/workflow/status/php-http/curl-client/Tests.svg?style=flat-square)](https://github.com/php-http/curl-client/actions?query=workflow%3ATests)
6
+ [![Total Downloads](https://img.shields.io/packagist/dt/php-http/curl-client.svg?style=flat-square)](https://packagist.org/packages/php-http/curl-client)
7
+
8
+ The cURL client use the cURL PHP extension which must be activated in your `php.ini`.
9
+
10
+
11
+ ## Install
12
+
13
+ Via Composer
14
+
15
+ ``` bash
16
+ $ composer require php-http/curl-client
17
+ ```
18
+
19
+ ## Documentation
20
+
21
+ Please see the [official documentation](http://docs.php-http.org/en/latest/clients/curl-client.html).
22
+
23
+ ## Testing
24
+
25
+ ``` bash
26
+ $ composer test
27
+ ```
28
+
29
+ ## Contributing
30
+
31
+ Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details.
32
+
33
+
34
+ ## Security
35
+
36
+ If you discover any security related issues, please contact us at
37
+ [security@php-http.org](mailto:security@php-http.org).
38
+
39
+
40
+ ## License
41
+
42
+ The MIT License (MIT). Please see [License File](LICENSE) for more information.
src/vendor/php-http/curl-client/composer.json ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "php-http/curl-client",
3
+ "description": "PSR-18 and HTTPlug Async client with cURL",
4
+ "license": "MIT",
5
+ "keywords": [
6
+ "curl",
7
+ "http",
8
+ "psr-18"
9
+ ],
10
+ "homepage": "http://php-http.org",
11
+ "authors": [{
12
+ "name": "Михаил Красильников",
13
+ "email": "m.krasilnikov@yandex.ru"
14
+ }],
15
+ "prefer-stable": true,
16
+ "minimum-stability": "dev",
17
+ "require": {
18
+ "php": "^7.1 || ^8.0",
19
+ "ext-curl": "*",
20
+ "php-http/discovery": "^1.6",
21
+ "php-http/httplug": "^2.0",
22
+ "php-http/message": "^1.2",
23
+ "psr/http-client": "^1.0",
24
+ "psr/http-factory": "^1.0",
25
+ "symfony/options-resolver": "^3.4 || ^4.0 || ^5.0 || ^6.0"
26
+ },
27
+ "require-dev": {
28
+ "guzzlehttp/psr7": "^1.0",
29
+ "php-http/client-integration-tests": "^3.0",
30
+ "phpunit/phpunit": "^7.5 || ^9.4",
31
+ "laminas/laminas-diactoros": "^2.0"
32
+ },
33
+ "autoload": {
34
+ "psr-4": {
35
+ "Http\\Client\\Curl\\": "src/"
36
+ }
37
+ },
38
+ "autoload-dev": {
39
+ "psr-4": {
40
+ "Http\\Client\\Curl\\Tests\\": "tests/"
41
+ }
42
+ },
43
+ "provide": {
44
+ "php-http/client-implementation": "1.0",
45
+ "php-http/async-client-implementation": "1.0",
46
+ "psr/http-client-implementation": "1.0"
47
+ },
48
+ "scripts": {
49
+ "test": "vendor/bin/phpunit",
50
+ "test-ci": "vendor/bin/phpunit --coverage-clover build/coverage.xml"
51
+ },
52
+ "extra": {
53
+ "branch-alias": {
54
+ "dev-master": "2.x-dev"
55
+ }
56
+ }
57
+ }
src/vendor/php-http/curl-client/puli.json ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "1.0",
3
+ "name": "php-http/curl-client",
4
+ "bindings": {
5
+ "98239b8b-103b-4f47-94c7-4cba49a05a1f": {
6
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
7
+ "class": "Http\\Client\\Curl\\Client",
8
+ "type": "Http\\Client\\HttpAsyncClient"
9
+ },
10
+ "a6a79968-2aa5-427c-bbe1-a581d9a48321": {
11
+ "_class": "Puli\\Discovery\\Binding\\ClassBinding",
12
+ "class": "Http\\Client\\Curl\\Client",
13
+ "type": "Http\\Client\\HttpClient"
14
+ }
15
+ },
16
+ "config": {
17
+ "bootstrap-file": "vendor/autoload.php"
18
+ },
19
+ "packages": {
20
+ "clue/stream-filter": {
21
+ "install-path": "vendor/clue/stream-filter",
22
+ "installer": "composer",
23
+ "env": "dev"
24
+ },
25
+ "doctrine/instantiator": {
26
+ "install-path": "vendor/doctrine/instantiator",
27
+ "installer": "composer",
28
+ "env": "dev"
29
+ },
30
+ "guzzlehttp/psr7": {
31
+ "install-path": "vendor/guzzlehttp/psr7",
32
+ "installer": "composer",
33
+ "env": "dev"
34
+ },
35
+ "justinrainbow/json-schema": {
36
+ "install-path": "vendor/justinrainbow/json-schema",
37
+ "installer": "composer",
38
+ "env": "dev"
39
+ },
40
+ "paragonie/random_compat": {
41
+ "install-path": "vendor/paragonie/random_compat",
42
+ "installer": "composer",
43
+ "env": "dev"
44
+ },
45
+ "php-http/adapter-integration-tests": {
46
+ "install-path": "vendor/php-http/adapter-integration-tests",
47
+ "installer": "composer",
48
+ "env": "dev"
49
+ },
50
+ "php-http/discovery": {
51
+ "install-path": "vendor/php-http/discovery",
52
+ "installer": "composer",
53
+ "env": "dev"
54
+ },
55
+ "php-http/httplug": {
56
+ "install-path": "vendor/php-http/httplug",
57
+ "installer": "composer"
58
+ },
59
+ "php-http/message": {
60
+ "install-path": "vendor/php-http/message",
61
+ "installer": "composer",
62
+ "env": "dev"
63
+ },
64
+ "php-http/message-factory": {
65
+ "install-path": "vendor/php-http/message-factory",
66
+ "installer": "composer"
67
+ },
68
+ "php-http/promise": {
69
+ "install-path": "vendor/php-http/promise",
70
+ "installer": "composer"
71
+ },
72
+ "phpdocumentor/reflection-docblock": {
73
+ "install-path": "vendor/phpdocumentor/reflection-docblock",
74
+ "installer": "composer",
75
+ "env": "dev"
76
+ },
77
+ "phpspec/prophecy": {
78
+ "install-path": "vendor/phpspec/prophecy",
79
+ "installer": "composer",
80
+ "env": "dev"
81
+ },
82
+ "phpunit/php-code-coverage": {
83
+ "install-path": "vendor/phpunit/php-code-coverage",
84
+ "installer": "composer",
85
+ "env": "dev"
86
+ },
87
+ "phpunit/php-file-iterator": {
88
+ "install-path": "vendor/phpunit/php-file-iterator",
89
+ "installer": "composer",
90
+ "env": "dev"
91
+ },
92
+ "phpunit/php-text-template": {
93
+ "install-path": "vendor/phpunit/php-text-template",
94
+ "installer": "composer",
95
+ "env": "dev"
96
+ },
97
+ "phpunit/php-timer": {
98
+ "install-path": "vendor/phpunit/php-timer",
99
+ "installer": "composer",
100
+ "env": "dev"
101
+ },
102
+ "phpunit/php-token-stream": {
103
+ "install-path": "vendor/phpunit/php-token-stream",
104
+ "installer": "composer",
105
+ "env": "dev"
106
+ },
107
+ "phpunit/phpunit": {
108
+ "install-path": "vendor/phpunit/phpunit",
109
+ "installer": "composer",
110
+ "env": "dev"
111
+ },
112
+ "phpunit/phpunit-mock-objects": {
113
+ "install-path": "vendor/phpunit/phpunit-mock-objects",
114
+ "installer": "composer",
115
+ "env": "dev"
116
+ },
117
+ "psr/http-message": {
118
+ "install-path": "vendor/psr/http-message",
119
+ "installer": "composer"
120
+ },
121
+ "psr/log": {
122
+ "install-path": "vendor/psr/log",
123
+ "installer": "composer",
124
+ "env": "dev"
125
+ },
126
+ "puli/composer-plugin": {
127
+ "install-path": "vendor/puli/composer-plugin",
128
+ "installer": "composer",
129
+ "env": "dev"
130
+ },
131
+ "puli/discovery": {
132
+ "install-path": "vendor/puli/discovery",
133
+ "installer": "composer",
134
+ "env": "dev"
135
+ },
136
+ "puli/repository": {
137
+ "install-path": "vendor/puli/repository",
138
+ "installer": "composer",
139
+ "env": "dev"
140
+ },
141
+ "puli/url-generator": {
142
+ "install-path": "vendor/puli/url-generator",
143
+ "installer": "composer",
144
+ "env": "dev"
145
+ },
146
+ "ramsey/uuid": {
147
+ "install-path": "vendor/ramsey/uuid",
148
+ "installer": "composer",
149
+ "env": "dev"
150
+ },
151
+ "sebastian/comparator": {
152
+ "install-path": "vendor/sebastian/comparator",
153
+ "installer": "composer",
154
+ "env": "dev"
155
+ },
156
+ "sebastian/diff": {
157
+ "install-path": "vendor/sebastian/diff",
158
+ "installer": "composer",
159
+ "env": "dev"
160
+ },
161
+ "sebastian/environment": {
162
+ "install-path": "vendor/sebastian/environment",
163
+ "installer": "composer",
164
+ "env": "dev"
165
+ },
166
+ "sebastian/exporter": {
167
+ "install-path": "vendor/sebastian/exporter",
168
+ "installer": "composer",
169
+ "env": "dev"
170
+ },
171
+ "sebastian/global-state": {
172
+ "install-path": "vendor/sebastian/global-state",
173
+ "installer": "composer",
174
+ "env": "dev"
175
+ },
176
+ "sebastian/recursion-context": {
177
+ "install-path": "vendor/sebastian/recursion-context",
178
+ "installer": "composer",
179
+ "env": "dev"
180
+ },
181
+ "sebastian/version": {
182
+ "install-path": "vendor/sebastian/version",
183
+ "installer": "composer",
184
+ "env": "dev"
185
+ },
186
+ "seld/jsonlint": {
187
+ "install-path": "vendor/seld/jsonlint",
188
+ "installer": "composer",
189
+ "env": "dev"
190
+ },
191
+ "symfony/filesystem": {
192
+ "install-path": "vendor/symfony/filesystem",
193
+ "installer": "composer",
194
+ "env": "dev"
195
+ },
196
+ "symfony/process": {
197
+ "install-path": "vendor/symfony/process",
198
+ "installer": "composer",
199
+ "env": "dev"
200
+ },
201
+ "symfony/yaml": {
202
+ "install-path": "vendor/symfony/yaml",
203
+ "installer": "composer",
204
+ "env": "dev"
205
+ },
206
+ "th3n3rd/cartesian-product": {
207
+ "install-path": "vendor/th3n3rd/cartesian-product",
208
+ "installer": "composer",
209
+ "env": "dev"
210
+ },
211
+ "webmozart/assert": {
212
+ "install-path": "vendor/webmozart/assert",
213
+ "installer": "composer",
214
+ "env": "dev"
215
+ },
216
+ "webmozart/expression": {
217
+ "install-path": "vendor/webmozart/expression",
218
+ "installer": "composer",
219
+ "env": "dev"
220
+ },
221
+ "webmozart/glob": {
222
+ "install-path": "vendor/webmozart/glob",
223
+ "installer": "composer",
224
+ "env": "dev"
225
+ },
226
+ "webmozart/json": {
227
+ "install-path": "vendor/webmozart/json",
228
+ "installer": "composer",
229
+ "env": "dev"
230
+ },
231
+ "webmozart/path-util": {
232
+ "install-path": "vendor/webmozart/path-util",
233
+ "installer": "composer",
234
+ "env": "dev"
235
+ },
236
+ "zendframework/zend-diactoros": {
237
+ "install-path": "vendor/zendframework/zend-diactoros",
238
+ "installer": "composer",
239
+ "env": "dev"
240
+ }
241
+ }
242
+ }
src/vendor/php-http/curl-client/src/Client.php ADDED
@@ -0,0 +1,398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Curl;
6
+
7
+ use Http\Client\Exception;
8
+ use Http\Client\HttpAsyncClient;
9
+ use Http\Client\HttpClient;
10
+ use Http\Discovery\Exception\NotFoundException;
11
+ use Http\Discovery\Psr17FactoryDiscovery;
12
+ use Http\Promise\Promise;
13
+ use Psr\Http\Message\RequestInterface;
14
+ use Psr\Http\Message\ResponseFactoryInterface;
15
+ use Psr\Http\Message\ResponseInterface;
16
+ use Psr\Http\Message\StreamFactoryInterface;
17
+ use Symfony\Component\OptionsResolver\OptionsResolver;
18
+
19
+ /**
20
+ * PSR-18 and HTTPlug Async client based on lib-curl.
21
+ *
22
+ * @license http://opensource.org/licenses/MIT MIT
23
+ * @author Михаил Красильников <m.krasilnikov@yandex.ru>
24
+ * @author Blake Williams <github@shabbyrobe.org>
25
+ *
26
+ * @api
27
+ *
28
+ * @since 1.0
29
+ */
30
+ class Client implements HttpClient, HttpAsyncClient
31
+ {
32
+ /**
33
+ * cURL options.
34
+ *
35
+ * @var array
36
+ */
37
+ private $curlOptions;
38
+
39
+ /**
40
+ * PSR-17 response factory.
41
+ *
42
+ * @var ResponseFactoryInterface
43
+ */
44
+ private $responseFactory;
45
+
46
+ /**
47
+ * PSR-17 stream factory.
48
+ *
49
+ * @var StreamFactoryInterface
50
+ */
51
+ private $streamFactory;
52
+
53
+ /**
54
+ * cURL synchronous requests handle.
55
+ *
56
+ * @var resource|\CurlHandle|null
57
+ */
58
+ private $handle;
59
+
60
+ /**
61
+ * Simultaneous requests runner.
62
+ *
63
+ * @var MultiRunner|null
64
+ */
65
+ private $multiRunner;
66
+
67
+ /**
68
+ * Create HTTP client.
69
+ *
70
+ * @param ResponseFactoryInterface|null $responseFactory PSR-17 HTTP response factory.
71
+ * @param StreamFactoryInterface|null $streamFactory PSR-17 HTTP stream factory.
72
+ * @param array $options cURL options
73
+ * {@link http://php.net/curl_setopt}.
74
+ *
75
+ * @throws NotFoundException If factory discovery failed.
76
+ *
77
+ * @since 2.0 Accepts PSR-17 factories instead of HTTPlug ones.
78
+ */
79
+ public function __construct(
80
+ ResponseFactoryInterface $responseFactory = null,
81
+ StreamFactoryInterface $streamFactory = null,
82
+ array $options = []
83
+ ) {
84
+ $this->responseFactory = $responseFactory ?: Psr17FactoryDiscovery::findResponseFactory();
85
+ $this->streamFactory = $streamFactory ?: Psr17FactoryDiscovery::findStreamFactory();
86
+ $resolver = new OptionsResolver();
87
+ $resolver->setDefaults(
88
+ [
89
+ CURLOPT_HEADER => false,
90
+ CURLOPT_RETURNTRANSFER => false,
91
+ CURLOPT_FOLLOWLOCATION => false
92
+ ]
93
+ );
94
+
95
+ // Our parsing will fail if this is set to true.
96
+ $resolver->setAllowedValues(
97
+ (string)CURLOPT_HEADER,
98
+ [false]
99
+ );
100
+
101
+ // Our parsing will fail if this is set to true.
102
+ $resolver->setAllowedValues(
103
+ (string)CURLOPT_RETURNTRANSFER,
104
+ [false]
105
+ );
106
+
107
+ // We do not know what everything curl supports and might support in the future.
108
+ // Make sure that we accept everything that is in the options.
109
+ $resolver->setDefined(array_keys($options));
110
+
111
+ $this->curlOptions = $resolver->resolve($options);
112
+ }
113
+
114
+ /**
115
+ * Release resources if still active.
116
+ */
117
+ public function __destruct()
118
+ {
119
+ if (is_resource($this->handle)) {
120
+ curl_close($this->handle);
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Sends a PSR-7 request and returns a PSR-7 response.
126
+ *
127
+ * @param RequestInterface $request
128
+ *
129
+ * @return ResponseInterface
130
+ *
131
+ * @throws \InvalidArgumentException For invalid header names or values.
132
+ * @throws \RuntimeException If creating the body stream fails.
133
+ * @throws Exception\NetworkException In case of network problems.
134
+ * @throws Exception\RequestException On invalid request.
135
+ *
136
+ * @since 1.6 \UnexpectedValueException replaced with RequestException
137
+ * @since 1.6 Throw NetworkException on network errors
138
+ * @since 1.0
139
+ */
140
+ public function sendRequest(RequestInterface $request): ResponseInterface
141
+ {
142
+ $responseBuilder = $this->createResponseBuilder();
143
+ $requestOptions = $this->prepareRequestOptions($request, $responseBuilder);
144
+
145
+ if (is_resource($this->handle)) {
146
+ curl_reset($this->handle);
147
+ } else {
148
+ $this->handle = curl_init();
149
+ }
150
+
151
+ curl_setopt_array($this->handle, $requestOptions);
152
+ curl_exec($this->handle);
153
+
154
+ $errno = curl_errno($this->handle);
155
+ switch ($errno) {
156
+ case CURLE_OK:
157
+ // All OK, no actions needed.
158
+ break;
159
+ case CURLE_COULDNT_RESOLVE_PROXY:
160
+ case CURLE_COULDNT_RESOLVE_HOST:
161
+ case CURLE_COULDNT_CONNECT:
162
+ case CURLE_OPERATION_TIMEOUTED:
163
+ case CURLE_SSL_CONNECT_ERROR:
164
+ throw new Exception\NetworkException(curl_error($this->handle), $request);
165
+ default:
166
+ throw new Exception\RequestException(curl_error($this->handle), $request);
167
+ }
168
+
169
+ $response = $responseBuilder->getResponse();
170
+ $response->getBody()->seek(0);
171
+
172
+ return $response;
173
+ }
174
+
175
+ /**
176
+ * Create builder to use for building response object.
177
+ *
178
+ * @return ResponseBuilder
179
+ */
180
+ private function createResponseBuilder(): ResponseBuilder
181
+ {
182
+ $body = $this->streamFactory->createStreamFromFile('php://temp', 'w+b');
183
+
184
+ $response = $this->responseFactory
185
+ ->createResponse(200)
186
+ ->withBody($body);
187
+
188
+ return new ResponseBuilder($response);
189
+ }
190
+
191
+ /**
192
+ * Update cURL options for given request and hook in the response builder.
193
+ *
194
+ * @param RequestInterface $request Request on which to create options.
195
+ * @param ResponseBuilder $responseBuilder Builder to use for building response.
196
+ *
197
+ * @return array cURL options based on request.
198
+ *
199
+ * @throws \InvalidArgumentException For invalid header names or values.
200
+ * @throws \RuntimeException If can not read body.
201
+ * @throws Exception\RequestException On invalid request.
202
+ */
203
+ private function prepareRequestOptions(
204
+ RequestInterface $request,
205
+ ResponseBuilder $responseBuilder
206
+ ): array {
207
+ $curlOptions = $this->curlOptions;
208
+
209
+ try {
210
+ $curlOptions[CURLOPT_HTTP_VERSION]
211
+ = $this->getProtocolVersion($request->getProtocolVersion());
212
+ } catch (\UnexpectedValueException $e) {
213
+ throw new Exception\RequestException($e->getMessage(), $request);
214
+ }
215
+ $curlOptions[CURLOPT_URL] = (string)$request->getUri();
216
+
217
+ $curlOptions = $this->addRequestBodyOptions($request, $curlOptions);
218
+
219
+ $curlOptions[CURLOPT_HTTPHEADER] = $this->createHeaders($request, $curlOptions);
220
+
221
+ if ($request->getUri()->getUserInfo()) {
222
+ $curlOptions[CURLOPT_USERPWD] = $request->getUri()->getUserInfo();
223
+ }
224
+
225
+ $curlOptions[CURLOPT_HEADERFUNCTION] = function ($ch, $data) use ($responseBuilder) {
226
+ $str = trim($data);
227
+ if ('' !== $str) {
228
+ if (stripos($str, 'http/') === 0) {
229
+ $responseBuilder->setStatus($str)->getResponse();
230
+ } else {
231
+ $responseBuilder->addHeader($str);
232
+ }
233
+ }
234
+
235
+ return strlen($data);
236
+ };
237
+
238
+ $curlOptions[CURLOPT_WRITEFUNCTION] = function ($ch, $data) use ($responseBuilder) {
239
+ return $responseBuilder->getResponse()->getBody()->write($data);
240
+ };
241
+
242
+ return $curlOptions;
243
+ }
244
+
245
+ /**
246
+ * Return cURL constant for specified HTTP version.
247
+ *
248
+ * @param string $requestVersion HTTP version ("1.0", "1.1" or "2.0").
249
+ *
250
+ * @return int Respective CURL_HTTP_VERSION_x_x constant.
251
+ *
252
+ * @throws \UnexpectedValueException If unsupported version requested.
253
+ */
254
+ private function getProtocolVersion(string $requestVersion): int
255
+ {
256
+ switch ($requestVersion) {
257
+ case '1.0':
258
+ return CURL_HTTP_VERSION_1_0;
259
+ case '1.1':
260
+ return CURL_HTTP_VERSION_1_1;
261
+ case '2.0':
262
+ if (defined('CURL_HTTP_VERSION_2_0')) {
263
+ return CURL_HTTP_VERSION_2_0;
264
+ }
265
+ throw new \UnexpectedValueException('libcurl 7.33 needed for HTTP 2.0 support');
266
+ }
267
+
268
+ return CURL_HTTP_VERSION_NONE;
269
+ }
270
+
271
+ /**
272
+ * Add request body related cURL options.
273
+ *
274
+ * @param RequestInterface $request Request on which to create options.
275
+ * @param array $curlOptions Options created by prepareRequestOptions().
276
+ *
277
+ * @return array cURL options based on request.
278
+ */
279
+ private function addRequestBodyOptions(RequestInterface $request, array $curlOptions): array
280
+ {
281
+ /*
282
+ * Some HTTP methods cannot have payload:
283
+ *
284
+ * - GET — cURL will automatically change method to PUT or POST if we set CURLOPT_UPLOAD or
285
+ * CURLOPT_POSTFIELDS.
286
+ * - HEAD — cURL treats HEAD as GET request with a same restrictions.
287
+ * - TRACE — According to RFC7231: a client MUST NOT send a message body in a TRACE request.
288
+ */
289
+ if (!in_array($request->getMethod(), ['GET', 'HEAD', 'TRACE'], true)) {
290
+ $body = $request->getBody();
291
+ $bodySize = $body->getSize();
292
+ if ($bodySize !== 0) {
293
+ if ($body->isSeekable()) {
294
+ $body->rewind();
295
+ }
296
+
297
+ // Message has non empty body.
298
+ if (null === $bodySize || $bodySize > 1024 * 1024) {
299
+ // Avoid full loading large or unknown size body into memory
300
+ $curlOptions[CURLOPT_UPLOAD] = true;
301
+ if (null !== $bodySize) {
302
+ $curlOptions[CURLOPT_INFILESIZE] = $bodySize;
303
+ }
304
+ $curlOptions[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
305
+ return $body->read($length);
306
+ };
307
+ } else {
308
+ // Small body can be loaded into memory
309
+ $curlOptions[CURLOPT_POSTFIELDS] = (string)$body;
310
+ }
311
+ }
312
+ }
313
+
314
+ if ($request->getMethod() === 'HEAD') {
315
+ // This will set HTTP method to "HEAD".
316
+ $curlOptions[CURLOPT_NOBODY] = true;
317
+ } elseif ($request->getMethod() !== 'GET') {
318
+ // GET is a default method. Other methods should be specified explicitly.
319
+ $curlOptions[CURLOPT_CUSTOMREQUEST] = $request->getMethod();
320
+ }
321
+
322
+ return $curlOptions;
323
+ }
324
+
325
+ /**
326
+ * Create headers array for CURLOPT_HTTPHEADER.
327
+ *
328
+ * @param RequestInterface $request Request on which to create headers.
329
+ * @param array $curlOptions Options created by prepareRequestOptions().
330
+ *
331
+ * @return string[]
332
+ */
333
+ private function createHeaders(RequestInterface $request, array $curlOptions): array
334
+ {
335
+ $curlHeaders = [];
336
+ $headers = $request->getHeaders();
337
+ foreach ($headers as $name => $values) {
338
+ $header = strtolower($name);
339
+ if ('expect' === $header) {
340
+ // curl-client does not support "Expect-Continue", so dropping "expect" headers
341
+ continue;
342
+ }
343
+ if ('content-length' === $header) {
344
+ if (array_key_exists(CURLOPT_POSTFIELDS, $curlOptions)) {
345
+ // Small body content length can be calculated here.
346
+ $values = [strlen($curlOptions[CURLOPT_POSTFIELDS])];
347
+ } elseif (!array_key_exists(CURLOPT_READFUNCTION, $curlOptions)) {
348
+ // Else if there is no body, forcing "Content-length" to 0
349
+ $values = [0];
350
+ }
351
+ }
352
+ foreach ($values as $value) {
353
+ $curlHeaders[] = $name . ': ' . $value;
354
+ }
355
+ }
356
+ /*
357
+ * curl-client does not support "Expect-Continue", but cURL adds "Expect" header by default.
358
+ * We can not suppress it, but we can set it to empty.
359
+ */
360
+ $curlHeaders[] = 'Expect:';
361
+
362
+ return $curlHeaders;
363
+ }
364
+
365
+ /**
366
+ * Sends a PSR-7 request in an asynchronous way.
367
+ *
368
+ * Exceptions related to processing the request are available from the returned Promise.
369
+ *
370
+ * @param RequestInterface $request
371
+ *
372
+ * @return Promise Resolves a PSR-7 Response or fails with an Http\Client\Exception.
373
+ *
374
+ * @throws \InvalidArgumentException For invalid header names or values.
375
+ * @throws \RuntimeException If creating the body stream fails.
376
+ * @throws Exception\RequestException On invalid request.
377
+ *
378
+ * @since 1.6 \UnexpectedValueException replaced with RequestException
379
+ * @since 1.0
380
+ */
381
+ public function sendAsyncRequest(RequestInterface $request)
382
+ {
383
+ if (!$this->multiRunner instanceof MultiRunner) {
384
+ $this->multiRunner = new MultiRunner();
385
+ }
386
+
387
+ $handle = curl_init();
388
+ $responseBuilder = $this->createResponseBuilder();
389
+ $requestOptions = $this->prepareRequestOptions($request, $responseBuilder);
390
+ curl_setopt_array($handle, $requestOptions);
391
+
392
+ $core = new PromiseCore($request, $handle, $responseBuilder);
393
+ $promise = new CurlPromise($core, $this->multiRunner);
394
+ $this->multiRunner->add($core);
395
+
396
+ return $promise;
397
+ }
398
+ }
src/vendor/php-http/curl-client/src/CurlPromise.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Curl;
6
+
7
+ use Http\Promise\Promise;
8
+
9
+ /**
10
+ * Promise represents a response that may not be available yet, but will be resolved at some point
11
+ * in future. It acts like a proxy to the actual response.
12
+ *
13
+ * This interface is an extension of the promises/a+ specification https://promisesaplus.com/
14
+ * Value is replaced by an object where its class implement a Psr\Http\Message\RequestInterface.
15
+ * Reason is replaced by an object where its class implement a Http\Client\Exception.
16
+ *
17
+ * @license http://opensource.org/licenses/MIT MIT
18
+ * @author Михаил Красильников <m.krasilnikov@yandex.ru>
19
+ */
20
+ class CurlPromise implements Promise
21
+ {
22
+ /**
23
+ * Shared promise core.
24
+ *
25
+ * @var PromiseCore
26
+ */
27
+ private $core;
28
+
29
+ /**
30
+ * Requests runner.
31
+ *
32
+ * @var MultiRunner
33
+ */
34
+ private $runner;
35
+
36
+ /**
37
+ * Create new promise.
38
+ *
39
+ * @param PromiseCore $core Shared promise core
40
+ * @param MultiRunner $runner Simultaneous requests runner
41
+ */
42
+ public function __construct(PromiseCore $core, MultiRunner $runner)
43
+ {
44
+ $this->core = $core;
45
+ $this->runner = $runner;
46
+ }
47
+
48
+ /**
49
+ * Add behavior for when the promise is resolved or rejected.
50
+ *
51
+ * If you do not care about one of the cases, you can set the corresponding callable to null
52
+ * The callback will be called when the response or exception arrived and never more than once.
53
+ *
54
+ * @param callable $onFulfilled Called when a response will be available
55
+ * @param callable $onRejected Called when an error happens.
56
+ *
57
+ * You must always return the Response in the interface or throw an Exception
58
+ *
59
+ * @return Promise Always returns a new promise which is resolved with value of the executed
60
+ * callback (onFulfilled / onRejected)
61
+ */
62
+ public function then(callable $onFulfilled = null, callable $onRejected = null)
63
+ {
64
+ if ($onFulfilled) {
65
+ $this->core->addOnFulfilled($onFulfilled);
66
+ }
67
+ if ($onRejected) {
68
+ $this->core->addOnRejected($onRejected);
69
+ }
70
+
71
+ return new self($this->core, $this->runner);
72
+ }
73
+
74
+ /**
75
+ * Get the state of the promise, one of PENDING, FULFILLED or REJECTED.
76
+ *
77
+ * @return string
78
+ */
79
+ public function getState()
80
+ {
81
+ return $this->core->getState();
82
+ }
83
+
84
+ /**
85
+ * Wait for the promise to be fulfilled or rejected.
86
+ *
87
+ * When this method returns, the request has been resolved and the appropriate callable has terminated.
88
+ *
89
+ * When called with the unwrap option
90
+ *
91
+ * @param bool $unwrap Whether to return resolved value / throw reason or not
92
+ *
93
+ * @return \Psr\Http\Message\ResponseInterface|null Resolved value, null if $unwrap is set to false
94
+ *
95
+ * @throws \Http\Client\Exception The rejection reason
96
+ */
97
+ public function wait($unwrap = true)
98
+ {
99
+ $this->runner->wait($this->core);
100
+
101
+ if ($unwrap) {
102
+ if ($this->core->getState() === self::REJECTED) {
103
+ throw $this->core->getException();
104
+ }
105
+
106
+ return $this->core->getResponse();
107
+ }
108
+
109
+ return null;
110
+ }
111
+ }
src/vendor/php-http/curl-client/src/MultiRunner.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Curl;
6
+
7
+ use Http\Client\Exception\RequestException;
8
+
9
+ /**
10
+ * Simultaneous requests runner.
11
+ *
12
+ * @license http://opensource.org/licenses/MIT MIT
13
+ * @author Михаил Красильников <m.krasilnikov@yandex.ru>
14
+ */
15
+ class MultiRunner
16
+ {
17
+ /**
18
+ * cURL multi handle.
19
+ *
20
+ * @var resource|null
21
+ */
22
+ private $multiHandle;
23
+
24
+ /**
25
+ * Awaiting cores.
26
+ *
27
+ * @var PromiseCore[]
28
+ */
29
+ private $cores = [];
30
+
31
+ /**
32
+ * Release resources if still active.
33
+ */
34
+ public function __destruct()
35
+ {
36
+ if (is_resource($this->multiHandle)) {
37
+ curl_multi_close($this->multiHandle);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Add promise to runner.
43
+ *
44
+ * @param PromiseCore $core
45
+ */
46
+ public function add(PromiseCore $core): void
47
+ {
48
+ foreach ($this->cores as $existed) {
49
+ if ($existed === $core) {
50
+ return;
51
+ }
52
+ }
53
+
54
+ $this->cores[] = $core;
55
+
56
+ if (null === $this->multiHandle) {
57
+ $this->multiHandle = curl_multi_init();
58
+ }
59
+ curl_multi_add_handle($this->multiHandle, $core->getHandle());
60
+ }
61
+
62
+ /**
63
+ * Remove promise from runner.
64
+ *
65
+ * @param PromiseCore $core
66
+ */
67
+ public function remove(PromiseCore $core): void
68
+ {
69
+ foreach ($this->cores as $index => $existed) {
70
+ if ($existed === $core) {
71
+ curl_multi_remove_handle($this->multiHandle, $core->getHandle());
72
+ unset($this->cores[$index]);
73
+
74
+ return;
75
+ }
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Wait for request(s) to be completed.
81
+ *
82
+ * @param PromiseCore|null $targetCore
83
+ */
84
+ public function wait(PromiseCore $targetCore = null): void
85
+ {
86
+ do {
87
+ $status = curl_multi_exec($this->multiHandle, $active);
88
+ $info = curl_multi_info_read($this->multiHandle);
89
+ if (false !== $info) {
90
+ $core = $this->findCoreByHandle($info['handle']);
91
+
92
+ if (null === $core) {
93
+ // We have no promise for this handle. Drop it.
94
+ curl_multi_remove_handle($this->multiHandle, $info['handle']);
95
+ continue;
96
+ }
97
+
98
+ if (CURLE_OK === $info['result']) {
99
+ $core->fulfill();
100
+ } else {
101
+ $error = curl_error($core->getHandle());
102
+ $core->reject(new RequestException($error, $core->getRequest()));
103
+ }
104
+ $this->remove($core);
105
+
106
+ // This is a promise we are waited for. So exiting wait().
107
+ if ($core === $targetCore) {
108
+ return;
109
+ }
110
+ }
111
+ } while ($status === CURLM_CALL_MULTI_PERFORM || $active);
112
+ }
113
+
114
+ /**
115
+ * Find core by handle.
116
+ *
117
+ * @param resource $handle
118
+ *
119
+ * @return PromiseCore|null
120
+ */
121
+ private function findCoreByHandle($handle): ?PromiseCore
122
+ {
123
+ foreach ($this->cores as $core) {
124
+ if ($core->getHandle() === $handle) {
125
+ return $core;
126
+ }
127
+ }
128
+
129
+ return null;
130
+ }
131
+ }
src/vendor/php-http/curl-client/src/PromiseCore.php ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Curl;
6
+
7
+ use Http\Client\Exception;
8
+ use Http\Promise\Promise;
9
+ use Psr\Http\Message\RequestInterface;
10
+ use Psr\Http\Message\ResponseInterface;
11
+
12
+ /**
13
+ * Shared promises core.
14
+ *
15
+ * @license http://opensource.org/licenses/MIT MIT
16
+ * @author Михаил Красильников <m.krasilnikov@yandex.ru>
17
+ */
18
+ class PromiseCore
19
+ {
20
+ /**
21
+ * HTTP request.
22
+ *
23
+ * @var RequestInterface
24
+ */
25
+ private $request;
26
+
27
+ /**
28
+ * cURL handle.
29
+ *
30
+ * @var resource
31
+ */
32
+ private $handle;
33
+
34
+ /**
35
+ * Response builder.
36
+ *
37
+ * @var ResponseBuilder
38
+ */
39
+ private $responseBuilder;
40
+
41
+ /**
42
+ * Promise state.
43
+ *
44
+ * @var string
45
+ */
46
+ private $state;
47
+
48
+ /**
49
+ * Exception.
50
+ *
51
+ * @var Exception|null
52
+ */
53
+ private $exception = null;
54
+
55
+ /**
56
+ * Functions to call when a response will be available.
57
+ *
58
+ * @var callable[]
59
+ */
60
+ private $onFulfilled = [];
61
+
62
+ /**
63
+ * Functions to call when an error happens.
64
+ *
65
+ * @var callable[]
66
+ */
67
+ private $onRejected = [];
68
+
69
+ /**
70
+ * Create shared core.
71
+ *
72
+ * @param RequestInterface $request HTTP request.
73
+ * @param resource|\CurlHandle $handle cURL handle.
74
+ * @param ResponseBuilder $responseBuilder Response builder.
75
+ *
76
+ * @throws \InvalidArgumentException If $handle is not a cURL resource.
77
+ */
78
+ public function __construct(
79
+ RequestInterface $request,
80
+ $handle,
81
+ ResponseBuilder $responseBuilder
82
+ ) {
83
+ if (PHP_MAJOR_VERSION === 7) {
84
+ if (!is_resource($handle)) {
85
+ throw new \InvalidArgumentException(
86
+ sprintf(
87
+ 'Parameter $handle expected to be a cURL resource, %s given',
88
+ gettype($handle)
89
+ )
90
+ );
91
+ } elseif (get_resource_type($handle) !== 'curl') {
92
+ throw new \InvalidArgumentException(
93
+ sprintf(
94
+ 'Parameter $handle expected to be a cURL resource, %s resource given',
95
+ get_resource_type($handle)
96
+ )
97
+ );
98
+ }
99
+ }
100
+
101
+ if (PHP_MAJOR_VERSION > 7 && !$handle instanceof \CurlHandle) {
102
+ throw new \InvalidArgumentException(
103
+ sprintf(
104
+ 'Parameter $handle expected to be a cURL resource, %s given',
105
+ get_debug_type($handle)
106
+ )
107
+ );
108
+ }
109
+
110
+ $this->request = $request;
111
+ $this->handle = $handle;
112
+ $this->responseBuilder = $responseBuilder;
113
+ $this->state = Promise::PENDING;
114
+ }
115
+
116
+ /**
117
+ * Add on fulfilled callback.
118
+ *
119
+ * @param callable $callback
120
+ */
121
+ public function addOnFulfilled(callable $callback): void
122
+ {
123
+ if ($this->getState() === Promise::PENDING) {
124
+ $this->onFulfilled[] = $callback;
125
+ } elseif ($this->getState() === Promise::FULFILLED) {
126
+ $response = call_user_func($callback, $this->responseBuilder->getResponse());
127
+ if ($response instanceof ResponseInterface) {
128
+ $this->responseBuilder->setResponse($response);
129
+ }
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Add on rejected callback.
135
+ *
136
+ * @param callable $callback
137
+ */
138
+ public function addOnRejected(callable $callback): void
139
+ {
140
+ if ($this->getState() === Promise::PENDING) {
141
+ $this->onRejected[] = $callback;
142
+ } elseif ($this->getState() === Promise::REJECTED) {
143
+ $this->exception = call_user_func($callback, $this->exception);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Return cURL handle.
149
+ *
150
+ * @return resource|\CurlHandle
151
+ */
152
+ public function getHandle()
153
+ {
154
+ return $this->handle;
155
+ }
156
+
157
+ /**
158
+ * Get the state of the promise, one of PENDING, FULFILLED or REJECTED.
159
+ *
160
+ * @return string
161
+ */
162
+ public function getState(): string
163
+ {
164
+ return $this->state;
165
+ }
166
+
167
+ /**
168
+ * Return request.
169
+ *
170
+ * @return RequestInterface
171
+ */
172
+ public function getRequest(): RequestInterface
173
+ {
174
+ return $this->request;
175
+ }
176
+
177
+ /**
178
+ * Return the value of the promise (fulfilled).
179
+ *
180
+ * @return ResponseInterface Response object only when the Promise is fulfilled
181
+ */
182
+ public function getResponse(): ResponseInterface
183
+ {
184
+ return $this->responseBuilder->getResponse();
185
+ }
186
+
187
+ /**
188
+ * Get the reason why the promise was rejected.
189
+ *
190
+ * If the exception is an instance of Http\Client\Exception\HttpException it will contain
191
+ * the response object with the status code and the http reason.
192
+ *
193
+ * @return \Throwable Exception Object only when the Promise is rejected
194
+ *
195
+ * @throws \LogicException When the promise is not rejected
196
+ */
197
+ public function getException(): \Throwable
198
+ {
199
+ if (null === $this->exception) {
200
+ throw new \LogicException('Promise is not rejected');
201
+ }
202
+
203
+ return $this->exception;
204
+ }
205
+
206
+ /**
207
+ * Fulfill promise.
208
+ */
209
+ public function fulfill(): void
210
+ {
211
+ $this->state = Promise::FULFILLED;
212
+ $response = $this->responseBuilder->getResponse();
213
+ try {
214
+ $response->getBody()->seek(0);
215
+ } catch (\RuntimeException $e) {
216
+ $exception = new Exception\TransferException($e->getMessage(), $e->getCode(), $e);
217
+ $this->reject($exception);
218
+
219
+ return;
220
+ }
221
+
222
+ while (count($this->onFulfilled) > 0) {
223
+ $callback = array_shift($this->onFulfilled);
224
+ $response = call_user_func($callback, $response);
225
+ }
226
+
227
+ if ($response instanceof ResponseInterface) {
228
+ $this->responseBuilder->setResponse($response);
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Reject promise.
234
+ *
235
+ * @param Exception $exception Reject reason
236
+ */
237
+ public function reject(Exception $exception): void
238
+ {
239
+ $this->exception = $exception;
240
+ $this->state = Promise::REJECTED;
241
+
242
+ while (count($this->onRejected) > 0) {
243
+ $callback = array_shift($this->onRejected);
244
+ try {
245
+ $exception = call_user_func($callback, $this->exception);
246
+ $this->exception = $exception;
247
+ } catch (Exception $exception) {
248
+ $this->exception = $exception;
249
+ }
250
+ }
251
+ }
252
+ }
src/vendor/php-http/curl-client/src/ResponseBuilder.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Http\Client\Curl;
6
+
7
+ use Http\Message\Builder\ResponseBuilder as OriginalResponseBuilder;
8
+ use Psr\Http\Message\ResponseInterface;
9
+
10
+ /**
11
+ * Extended response builder.
12
+ */
13
+ class ResponseBuilder extends OriginalResponseBuilder
14
+ {
15
+ /**
16
+ * Replace response with a new instance.
17
+ *
18
+ * @param ResponseInterface $response
19
+ */
20
+ public function setResponse(ResponseInterface $response): void
21
+ {
22
+ $this->response = $response;
23
+ }
24
+ }
src/vendor/php-http/discovery/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
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
1
  # Change Log
2
 
3
+ ## 1.14.3 - 2022-07-11
4
+
5
+ - [#207](https://github.com/php-http/discovery/pull/207) - Updates Exception to extend Throwable solving static analysis errors for consumers
6
+
7
  ## 1.14.2 - 2022-05-25
8
 
9
  - [#202](https://github.com/php-http/discovery/pull/202) - Avoid error when the Symfony PSR-18 client exists but its dependencies are not installed
src/vendor/php-http/discovery/src/Exception.php CHANGED
@@ -2,11 +2,13 @@
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
  }
2
 
3
  namespace Http\Discovery;
4
 
5
+ use Throwable;
6
+
7
  /**
8
  * An interface implemented by all discovery related exceptions.
9
  *
10
  * @author Tobias Nyholm <tobias.nyholm@gmail.com>
11
  */
12
+ interface Exception extends Throwable
13
  {
14
  }
src/vendor/psr/container/.gitignore DELETED
@@ -1,3 +0,0 @@
1
- composer.lock
2
- composer.phar
3
- /vendor/
 
 
 
src/vendor/psr/container/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2013-2016 container-interop
4
- Copyright (c) 2016 PHP Framework Interoperability Group
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy of
7
- this software and associated documentation files (the "Software"), to deal in
8
- the Software without restriction, including without limitation the rights to
9
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
- the Software, and to permit persons to whom the Software is furnished to do so,
11
- subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/psr/container/README.md DELETED
@@ -1,13 +0,0 @@
1
- Container interface
2
- ==============
3
-
4
- This repository holds all interfaces related to [PSR-11 (Container Interface)][psr-url].
5
-
6
- Note that this is not a Container implementation of its own. It is merely abstractions that describe the components of a Dependency Injection Container.
7
-
8
- The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist.
9
-
10
- [psr-url]: https://www.php-fig.org/psr/psr-11/
11
- [package-url]: https://packagist.org/packages/psr/container
12
- [implementation-url]: https://packagist.org/providers/psr/container-implementation
13
-
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/psr/container/composer.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "name": "psr/container",
3
- "type": "library",
4
- "description": "Common Container Interface (PHP FIG PSR-11)",
5
- "keywords": ["psr", "psr-11", "container", "container-interop", "container-interface"],
6
- "homepage": "https://github.com/php-fig/container",
7
- "license": "MIT",
8
- "authors": [
9
- {
10
- "name": "PHP-FIG",
11
- "homepage": "https://www.php-fig.org/"
12
- }
13
- ],
14
- "require": {
15
- "php": ">=7.4.0"
16
- },
17
- "autoload": {
18
- "psr-4": {
19
- "Psr\\Container\\": "src/"
20
- }
21
- }
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/psr/container/src/ContainerExceptionInterface.php DELETED
@@ -1,12 +0,0 @@
1
- <?php
2
-
3
- namespace Psr\Container;
4
-
5
- use Throwable;
6
-
7
- /**
8
- * Base interface representing a generic exception in a container.
9
- */
10
- interface ContainerExceptionInterface extends Throwable
11
- {
12
- }
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/psr/container/src/ContainerInterface.php DELETED
@@ -1,36 +0,0 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Psr\Container;
6
-
7
- /**
8
- * Describes the interface of a container that exposes methods to read its entries.
9
- */
10
- interface ContainerInterface
11
- {
12
- /**
13
- * Finds an entry of the container by its identifier and returns it.
14
- *
15
- * @param string $id Identifier of the entry to look for.
16
- *
17
- * @throws NotFoundExceptionInterface No entry was found for **this** identifier.
18
- * @throws ContainerExceptionInterface Error while retrieving the entry.
19
- *
20
- * @return mixed Entry.
21
- */
22
- public function get(string $id);
23
-
24
- /**
25
- * Returns true if the container can return an entry for the given identifier.
26
- * Returns false otherwise.
27
- *
28
- * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
29
- * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
30
- *
31
- * @param string $id Identifier of the entry to look for.
32
- *
33
- * @return bool
34
- */
35
- public function has(string $id);
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/psr/container/src/NotFoundExceptionInterface.php DELETED
@@ -1,10 +0,0 @@
1
- <?php
2
-
3
- namespace Psr\Container;
4
-
5
- /**
6
- * No entry was found in the container.
7
- */
8
- interface NotFoundExceptionInterface extends ContainerExceptionInterface
9
- {
10
- }
 
 
 
 
 
 
 
 
 
 
src/vendor/sentry/sentry/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
 
3
  ## Unreleased
4
 
 
 
 
 
 
 
 
 
 
5
  ## 3.6.0 (2022-06-10)
6
 
7
  - Add support for `monolog/monolog:^3.0` (#1321)
2
 
3
  ## Unreleased
4
 
5
+ ## 3.7.0 (2022-07-18)
6
+
7
+ - Fix `Scope::getTransaction()` so that it returns also unsampled transactions (#1334)
8
+ - Set the event extras by taking the data from the Monolog record's extra (#1330)
9
+
10
+ ## 3.6.1 (2022-06-27)
11
+
12
+ - Set the `sentry-trace` header when using the tracing middleware (#1331)
13
+
14
  ## 3.6.0 (2022-06-10)
15
 
16
  - Add support for `monolog/monolog:^3.0` (#1321)
src/vendor/sentry/sentry/composer.json CHANGED
@@ -96,7 +96,7 @@
96
  "prefer-stable": true,
97
  "extra": {
98
  "branch-alias": {
99
- "dev-master": "3.6.x-dev"
100
  }
101
  }
102
  }
96
  "prefer-stable": true,
97
  "extra": {
98
  "branch-alias": {
99
+ "dev-master": "3.7.x-dev"
100
  }
101
  }
102
  }
src/vendor/sentry/sentry/src/Monolog/Handler.php CHANGED
@@ -69,10 +69,16 @@ final class Handler extends AbstractProcessingHandler
69
 
70
  $monologContextData = $this->getMonologContextData($record['context']);
71
 
72
- if (!empty($monologContextData)) {
73
  $scope->setExtra('monolog.context', $monologContextData);
74
  }
75
 
 
 
 
 
 
 
76
  $this->hub->captureEvent($event, $hint);
77
  });
78
  }
@@ -101,4 +107,24 @@ final class Handler extends AbstractProcessingHandler
101
 
102
  return $contextData;
103
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  }
69
 
70
  $monologContextData = $this->getMonologContextData($record['context']);
71
 
72
+ if ([] !== $monologContextData) {
73
  $scope->setExtra('monolog.context', $monologContextData);
74
  }
75
 
76
+ $monologExtraData = $this->getMonologExtraData($record['extra']);
77
+
78
+ if ([] !== $monologExtraData) {
79
+ $scope->setExtra('monolog.extra', $monologExtraData);
80
+ }
81
+
82
  $this->hub->captureEvent($event, $hint);
83
  });
84
  }
107
 
108
  return $contextData;
109
  }
110
+
111
+ /**
112
+ * @param mixed[] $context
113
+ *
114
+ * @return mixed[]
115
+ */
116
+ private function getMonologExtraData(array $context): array
117
+ {
118
+ if (!$this->fillExtraContext) {
119
+ return [];
120
+ }
121
+
122
+ $extraData = [];
123
+
124
+ foreach ($context as $key => $value) {
125
+ $extraData[$key] = $value;
126
+ }
127
+
128
+ return $extraData;
129
+ }
130
  }
src/vendor/sentry/sentry/src/State/Scope.php CHANGED
@@ -408,12 +408,8 @@ final class Scope
408
  */
409
  public function getTransaction(): ?Transaction
410
  {
411
- $span = $this->span;
412
-
413
- if (null !== $span && null !== $span->getSpanRecorder() && !empty($span->getSpanRecorder()->getSpans())) {
414
- // The first span in the recorder is considered to be a Transaction
415
- /** @var Transaction */
416
- return $span->getSpanRecorder()->getSpans()[0];
417
  }
418
 
419
  return null;
408
  */
409
  public function getTransaction(): ?Transaction
410
  {
411
+ if (null !== $this->span) {
412
+ return $this->span->getTransaction();
 
 
 
 
413
  }
414
 
415
  return null;
src/vendor/sentry/sentry/src/Tracing/GuzzleTracingMiddleware.php CHANGED
@@ -34,7 +34,7 @@ final class GuzzleTracingMiddleware
34
 
35
  $childSpan = $span->startChild($spanContext);
36
 
37
- $request->withHeader('sentry-trace', $childSpan->toTraceparent());
38
 
39
  $handlerPromiseCallback = static function ($responseOrException) use ($hub, $request, $childSpan) {
40
  // We finish the span (which means setting the span end timestamp) first to ensure the measured time
34
 
35
  $childSpan = $span->startChild($spanContext);
36
 
37
+ $request = $request->withHeader('sentry-trace', $childSpan->toTraceparent());
38
 
39
  $handlerPromiseCallback = static function ($responseOrException) use ($hub, $request, $childSpan) {
40
  // We finish the span (which means setting the span end timestamp) first to ensure the measured time
src/vendor/sentry/sentry/src/Tracing/Span.php CHANGED
@@ -7,7 +7,7 @@ namespace Sentry\Tracing;
7
  use Sentry\EventId;
8
 
9
  /**
10
- * This class stores all the information about a Span.
11
  */
12
  class Span
13
  {
@@ -22,22 +22,22 @@ class Span
22
  protected $traceId;
23
 
24
  /**
25
- * @var string|null Description of the Span
26
  */
27
  protected $description;
28
 
29
  /**
30
- * @var string|null Operation of the Span
31
  */
32
  protected $op;
33
 
34
  /**
35
- * @var SpanStatus|null Completion status of the Span
36
  */
37
  protected $status;
38
 
39
  /**
40
- * @var SpanId|null ID of the parent Span
41
  */
42
  protected $parentSpanId;
43
 
@@ -47,7 +47,7 @@ class Span
47
  protected $sampled;
48
 
49
  /**
50
- * @var array<string, string> A List of tags associated to this Span
51
  */
52
  protected $tags = [];
53
 
@@ -67,10 +67,15 @@ class Span
67
  protected $endTimestamp;
68
 
69
  /**
70
- * @var SpanRecorder|null Reference instance to the SpanRecorder
71
  */
72
  protected $spanRecorder;
73
 
 
 
 
 
 
74
  /**
75
  * Constructor.
76
  *
@@ -390,6 +395,7 @@ class Span
390
  $context->setTraceId($this->traceId);
391
 
392
  $span = new self($context);
 
393
  $span->spanRecorder = $this->spanRecorder;
394
 
395
  if (null != $span->spanRecorder) {
@@ -417,6 +423,14 @@ class Span
417
  $this->spanRecorder = null;
418
  }
419
 
 
 
 
 
 
 
 
 
420
  /**
421
  * Returns a string that can be used for the `sentry-trace` header.
422
  */
7
  use Sentry\EventId;
8
 
9
  /**
10
+ * This class stores all the information about a span.
11
  */
12
  class Span
13
  {
22
  protected $traceId;
23
 
24
  /**
25
+ * @var string|null Description of the span
26
  */
27
  protected $description;
28
 
29
  /**
30
+ * @var string|null Operation of the span
31
  */
32
  protected $op;
33
 
34
  /**
35
+ * @var SpanStatus|null Completion status of the span
36
  */
37
  protected $status;
38
 
39
  /**
40
+ * @var SpanId|null ID of the parent span
41
  */
42
  protected $parentSpanId;
43
 
47
  protected $sampled;
48
 
49
  /**
50
+ * @var array<string, string> A List of tags associated to this span
51
  */
52
  protected $tags = [];
53
 
67
  protected $endTimestamp;
68
 
69
  /**
70
+ * @var SpanRecorder|null Reference instance to the {@see SpanRecorder}
71
  */
72
  protected $spanRecorder;
73
 
74
+ /**
75
+ * @var Transaction|null The transaction containing this span
76
+ */
77
+ protected $transaction;
78
+
79
  /**
80
  * Constructor.
81
  *
395
  $context->setTraceId($this->traceId);
396
 
397
  $span = new self($context);
398
+ $span->transaction = $this->transaction;
399
  $span->spanRecorder = $this->spanRecorder;
400
 
401
  if (null != $span->spanRecorder) {
423
  $this->spanRecorder = null;
424
  }
425
 
426
+ /**
427
+ * Returns the transaction containing this span.
428
+ */
429
+ public function getTransaction(): ?Transaction
430
+ {
431
+ return $this->transaction;
432
+ }
433
+
434
  /**
435
  * Returns a string that can be used for the `sentry-trace` header.
436
  */
src/vendor/sentry/sentry/src/Tracing/Transaction.php CHANGED
@@ -38,6 +38,7 @@ final class Transaction extends Span
38
 
39
  $this->hub = $hub ?? SentrySdk::getCurrentHub();
40
  $this->name = $context->getName();
 
41
  }
42
 
43
  /**
38
 
39
  $this->hub = $hub ?? SentrySdk::getCurrentHub();
40
  $this->name = $context->getName();
41
+ $this->transaction = $this;
42
  }
43
 
44
  /**
src/vendor/symfony/http-client-contracts/.gitignore DELETED
@@ -1,3 +0,0 @@
1
- vendor/
2
- composer.lock
3
- phpunit.xml
 
 
 
src/vendor/symfony/http-client-contracts/CHANGELOG.md DELETED
@@ -1,5 +0,0 @@
1
- CHANGELOG
2
- =========
3
-
4
- The changelog is maintained for all Symfony contracts at the following URL:
5
- https://github.com/symfony/contracts/blob/main/CHANGELOG.md
 
 
 
 
 
src/vendor/symfony/http-client-contracts/ChunkInterface.php DELETED
@@ -1,71 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
15
-
16
- /**
17
- * The interface of chunks returned by ResponseStreamInterface::current().
18
- *
19
- * When the chunk is first, last or timeout, the content MUST be empty.
20
- * When an unchecked timeout or a network error occurs, a TransportExceptionInterface
21
- * MUST be thrown by the destructor unless one was already thrown by another method.
22
- *
23
- * @author Nicolas Grekas <p@tchwork.com>
24
- */
25
- interface ChunkInterface
26
- {
27
- /**
28
- * Tells when the idle timeout has been reached.
29
- *
30
- * @throws TransportExceptionInterface on a network error
31
- */
32
- public function isTimeout(): bool;
33
-
34
- /**
35
- * Tells when headers just arrived.
36
- *
37
- * @throws TransportExceptionInterface on a network error or when the idle timeout is reached
38
- */
39
- public function isFirst(): bool;
40
-
41
- /**
42
- * Tells when the body just completed.
43
- *
44
- * @throws TransportExceptionInterface on a network error or when the idle timeout is reached
45
- */
46
- public function isLast(): bool;
47
-
48
- /**
49
- * Returns a [status code, headers] tuple when a 1xx status code was just received.
50
- *
51
- * @throws TransportExceptionInterface on a network error or when the idle timeout is reached
52
- */
53
- public function getInformationalStatus(): ?array;
54
-
55
- /**
56
- * Returns the content of the response chunk.
57
- *
58
- * @throws TransportExceptionInterface on a network error or when the idle timeout is reached
59
- */
60
- public function getContent(): string;
61
-
62
- /**
63
- * Returns the offset of the chunk in the response body.
64
- */
65
- public function getOffset(): int;
66
-
67
- /**
68
- * In case of error, returns the message that describes it.
69
- */
70
- public function getError(): ?string;
71
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Exception/ClientExceptionInterface.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Exception;
13
-
14
- /**
15
- * When a 4xx response is returned.
16
- *
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- interface ClientExceptionInterface extends HttpExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Exception/DecodingExceptionInterface.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Exception;
13
-
14
- /**
15
- * When a content-type cannot be decoded to the expected representation.
16
- *
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- interface DecodingExceptionInterface extends ExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Exception/ExceptionInterface.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Exception;
13
-
14
- /**
15
- * The base interface for all exceptions in the contract.
16
- *
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- interface ExceptionInterface extends \Throwable
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Exception/HttpExceptionInterface.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\ResponseInterface;
15
-
16
- /**
17
- * Base interface for HTTP-related exceptions.
18
- *
19
- * @author Anton Chernikov <anton_ch1989@mail.ru>
20
- */
21
- interface HttpExceptionInterface extends ExceptionInterface
22
- {
23
- public function getResponse(): ResponseInterface;
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Exception/RedirectionExceptionInterface.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Exception;
13
-
14
- /**
15
- * When a 3xx response is returned and the "max_redirects" option has been reached.
16
- *
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- interface RedirectionExceptionInterface extends HttpExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Exception/ServerExceptionInterface.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Exception;
13
-
14
- /**
15
- * When a 5xx response is returned.
16
- *
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- interface ServerExceptionInterface extends HttpExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Exception/TimeoutExceptionInterface.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Exception;
13
-
14
- /**
15
- * When an idle timeout occurs.
16
- *
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- interface TimeoutExceptionInterface extends TransportExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Exception/TransportExceptionInterface.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Exception;
13
-
14
- /**
15
- * When any error happens at the transport level.
16
- *
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- interface TransportExceptionInterface extends ExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/HttpClientInterface.php DELETED
@@ -1,95 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
15
- use Symfony\Contracts\HttpClient\Test\HttpClientTestCase;
16
-
17
- /**
18
- * Provides flexible methods for requesting HTTP resources synchronously or asynchronously.
19
- *
20
- * @see HttpClientTestCase for a reference test suite
21
- *
22
- * @method static withOptions(array $options) Returns a new instance of the client with new default options
23
- *
24
- * @author Nicolas Grekas <p@tchwork.com>
25
- */
26
- interface HttpClientInterface
27
- {
28
- public const OPTIONS_DEFAULTS = [
29
- 'auth_basic' => null, // array|string - an array containing the username as first value, and optionally the
30
- // password as the second one; or string like username:password - enabling HTTP Basic
31
- // authentication (RFC 7617)
32
- 'auth_bearer' => null, // string - a token enabling HTTP Bearer authorization (RFC 6750)
33
- 'query' => [], // string[] - associative array of query string values to merge with the request's URL
34
- 'headers' => [], // iterable|string[]|string[][] - headers names provided as keys or as part of values
35
- 'body' => '', // array|string|resource|\Traversable|\Closure - the callback SHOULD yield a string
36
- // smaller than the amount requested as argument; the empty string signals EOF; if
37
- // an array is passed, it is meant as a form payload of field names and values
38
- 'json' => null, // mixed - if set, implementations MUST set the "body" option to the JSON-encoded
39
- // value and set the "content-type" header to a JSON-compatible value if it is not
40
- // explicitly defined in the headers option - typically "application/json"
41
- 'user_data' => null, // mixed - any extra data to attach to the request (scalar, callable, object...) that
42
- // MUST be available via $response->getInfo('user_data') - not used internally
43
- 'max_redirects' => 20, // int - the maximum number of redirects to follow; a value lower than or equal to 0
44
- // means redirects should not be followed; "Authorization" and "Cookie" headers MUST
45
- // NOT follow except for the initial host name
46
- 'http_version' => null, // string - defaults to the best supported version, typically 1.1 or 2.0
47
- 'base_uri' => null, // string - the URI to resolve relative URLs, following rules in RFC 3986, section 2
48
- 'buffer' => true, // bool|resource|\Closure - whether the content of the response should be buffered or not,
49
- // or a stream resource where the response body should be written,
50
- // or a closure telling if/where the response should be buffered based on its headers
51
- 'on_progress' => null, // callable(int $dlNow, int $dlSize, array $info) - throwing any exceptions MUST abort
52
- // the request; it MUST be called on DNS resolution, on arrival of headers and on
53
- // completion; it SHOULD be called on upload/download of data and at least 1/s
54
- 'resolve' => [], // string[] - a map of host to IP address that SHOULD replace DNS resolution
55
- 'proxy' => null, // string - by default, the proxy-related env vars handled by curl SHOULD be honored
56
- 'no_proxy' => null, // string - a comma separated list of hosts that do not require a proxy to be reached
57
- 'timeout' => null, // float - the idle timeout - defaults to ini_get('default_socket_timeout')
58
- 'max_duration' => 0, // float - the maximum execution time for the request+response as a whole;
59
- // a value lower than or equal to 0 means it is unlimited
60
- 'bindto' => '0', // string - the interface or the local socket to bind to
61
- 'verify_peer' => true, // see https://php.net/context.ssl for the following options
62
- 'verify_host' => true,
63
- 'cafile' => null,
64
- 'capath' => null,
65
- 'local_cert' => null,
66
- 'local_pk' => null,
67
- 'passphrase' => null,
68
- 'ciphers' => null,
69
- 'peer_fingerprint' => null,
70
- 'capture_peer_cert_chain' => false,
71
- 'extra' => [], // array - additional options that can be ignored if unsupported, unlike regular options
72
- ];
73
-
74
- /**
75
- * Requests an HTTP resource.
76
- *
77
- * Responses MUST be lazy, but their status code MUST be
78
- * checked even if none of their public methods are called.
79
- *
80
- * Implementations are not required to support all options described above; they can also
81
- * support more custom options; but in any case, they MUST throw a TransportExceptionInterface
82
- * when an unsupported option is passed.
83
- *
84
- * @throws TransportExceptionInterface When an unsupported option is passed
85
- */
86
- public function request(string $method, string $url, array $options = []): ResponseInterface;
87
-
88
- /**
89
- * Yields responses chunk by chunk as they complete.
90
- *
91
- * @param ResponseInterface|iterable<array-key, ResponseInterface> $responses One or more responses created by the current HTTP client
92
- * @param float|null $timeout The idle timeout before yielding timeout chunks
93
- */
94
- public function stream($responses, float $timeout = null): ResponseStreamInterface;
95
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/LICENSE DELETED
@@ -1,19 +0,0 @@
1
- Copyright (c) 2018-2022 Fabien Potencier
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/symfony/http-client-contracts/README.md DELETED
@@ -1,9 +0,0 @@
1
- Symfony HttpClient Contracts
2
- ============================
3
-
4
- A set of abstractions extracted out of the Symfony components.
5
-
6
- Can be used to build on semantics that the Symfony components proved useful - and
7
- that already have battle tested implementations.
8
-
9
- See https://github.com/symfony/contracts/blob/main/README.md for more information.
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/ResponseInterface.php DELETED
@@ -1,109 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
15
- use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
16
- use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
17
- use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
18
- use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
19
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
20
-
21
- /**
22
- * A (lazily retrieved) HTTP response.
23
- *
24
- * @author Nicolas Grekas <p@tchwork.com>
25
- */
26
- interface ResponseInterface
27
- {
28
- /**
29
- * Gets the HTTP status code of the response.
30
- *
31
- * @throws TransportExceptionInterface when a network error occurs
32
- */
33
- public function getStatusCode(): int;
34
-
35
- /**
36
- * Gets the HTTP headers of the response.
37
- *
38
- * @param bool $throw Whether an exception should be thrown on 3/4/5xx status codes
39
- *
40
- * @return string[][] The headers of the response keyed by header names in lowercase
41
- *
42
- * @throws TransportExceptionInterface When a network error occurs
43
- * @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
44
- * @throws ClientExceptionInterface On a 4xx when $throw is true
45
- * @throws ServerExceptionInterface On a 5xx when $throw is true
46
- */
47
- public function getHeaders(bool $throw = true): array;
48
-
49
- /**
50
- * Gets the response body as a string.
51
- *
52
- * @param bool $throw Whether an exception should be thrown on 3/4/5xx status codes
53
- *
54
- * @throws TransportExceptionInterface When a network error occurs
55
- * @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
56
- * @throws ClientExceptionInterface On a 4xx when $throw is true
57
- * @throws ServerExceptionInterface On a 5xx when $throw is true
58
- */
59
- public function getContent(bool $throw = true): string;
60
-
61
- /**
62
- * Gets the response body decoded as array, typically from a JSON payload.
63
- *
64
- * @param bool $throw Whether an exception should be thrown on 3/4/5xx status codes
65
- *
66
- * @throws DecodingExceptionInterface When the body cannot be decoded to an array
67
- * @throws TransportExceptionInterface When a network error occurs
68
- * @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
69
- * @throws ClientExceptionInterface On a 4xx when $throw is true
70
- * @throws ServerExceptionInterface On a 5xx when $throw is true
71
- */
72
- public function toArray(bool $throw = true): array;
73
-
74
- /**
75
- * Closes the response stream and all related buffers.
76
- *
77
- * No further chunk will be yielded after this method has been called.
78
- */
79
- public function cancel(): void;
80
-
81
- /**
82
- * Returns info coming from the transport layer.
83
- *
84
- * This method SHOULD NOT throw any ExceptionInterface and SHOULD be non-blocking.
85
- * The returned info is "live": it can be empty and can change from one call to
86
- * another, as the request/response progresses.
87
- *
88
- * The following info MUST be returned:
89
- * - canceled (bool) - true if the response was canceled using ResponseInterface::cancel(), false otherwise
90
- * - error (string|null) - the error message when the transfer was aborted, null otherwise
91
- * - http_code (int) - the last response code or 0 when it is not known yet
92
- * - http_method (string) - the HTTP verb of the last request
93
- * - redirect_count (int) - the number of redirects followed while executing the request
94
- * - redirect_url (string|null) - the resolved location of redirect responses, null otherwise
95
- * - response_headers (array) - an array modelled after the special $http_response_header variable
96
- * - start_time (float) - the time when the request was sent or 0.0 when it's pending
97
- * - url (string) - the last effective URL of the request
98
- * - user_data (mixed) - the value of the "user_data" request option, null if not set
99
- *
100
- * When the "capture_peer_cert_chain" option is true, the "peer_certificate_chain"
101
- * attribute SHOULD list the peer certificates as an array of OpenSSL X.509 resources.
102
- *
103
- * Other info SHOULD be named after curl_getinfo()'s associative return value.
104
- *
105
- * @return mixed An array of all available info, or one of them when $type is
106
- * provided, or null when an unsupported type is requested
107
- */
108
- public function getInfo(string $type = null);
109
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/ResponseStreamInterface.php DELETED
@@ -1,26 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient;
13
-
14
- /**
15
- * Yields response chunks, returned by HttpClientInterface::stream().
16
- *
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- *
19
- * @extends \Iterator<ResponseInterface, ChunkInterface>
20
- */
21
- interface ResponseStreamInterface extends \Iterator
22
- {
23
- public function key(): ResponseInterface;
24
-
25
- public function current(): ChunkInterface;
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Test/Fixtures/web/index.php DELETED
@@ -1,192 +0,0 @@
1
- <?php
2
-
3
- if ('cli-server' !== \PHP_SAPI) {
4
- // safe guard against unwanted execution
5
- throw new \Exception("You cannot run this script directly, it's a fixture for TestHttpServer.");
6
- }
7
-
8
- $vars = [];
9
-
10
- if (!$_POST) {
11
- $_POST = json_decode(file_get_contents('php://input'), true);
12
- $_POST['content-type'] = $_SERVER['HTTP_CONTENT_TYPE'] ?? '?';
13
- }
14
-
15
- foreach ($_SERVER as $k => $v) {
16
- switch ($k) {
17
- default:
18
- if (0 !== strpos($k, 'HTTP_')) {
19
- continue 2;
20
- }
21
- // no break
22
- case 'SERVER_NAME':
23
- case 'SERVER_PROTOCOL':
24
- case 'REQUEST_URI':
25
- case 'REQUEST_METHOD':
26
- case 'PHP_AUTH_USER':
27
- case 'PHP_AUTH_PW':
28
- $vars[$k] = $v;
29
- }
30
- }
31
-
32
- $json = json_encode($vars, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE);
33
-
34
- switch ($vars['REQUEST_URI']) {
35
- default:
36
- exit;
37
-
38
- case '/head':
39
- header('Content-Length: '.strlen($json), true);
40
- break;
41
-
42
- case '/':
43
- case '/?a=a&b=b':
44
- case 'http://127.0.0.1:8057/':
45
- case 'http://localhost:8057/':
46
- ob_start('ob_gzhandler');
47
- break;
48
-
49
- case '/103':
50
- header('HTTP/1.1 103 Early Hints');
51
- header('Link: </style.css>; rel=preload; as=style', false);
52
- header('Link: </script.js>; rel=preload; as=script', false);
53
- flush();
54
- usleep(1000);
55
- echo "HTTP/1.1 200 OK\r\n";
56
- echo "Date: Fri, 26 May 2017 10:02:11 GMT\r\n";
57
- echo "Content-Length: 13\r\n";
58
- echo "\r\n";
59
- echo 'Here the body';
60
- exit;
61
-
62
- case '/404':
63
- header('Content-Type: application/json', true, 404);
64
- break;
65
-
66
- case '/404-gzipped':
67
- header('Content-Type: text/plain', true, 404);
68
- ob_start('ob_gzhandler');
69
- @ob_flush();
70
- flush();
71
- usleep(300000);
72
- echo 'some text';
73
- exit;
74
-
75
- case '/301':
76
- if ('Basic Zm9vOmJhcg==' === $vars['HTTP_AUTHORIZATION']) {
77
- header('Location: http://127.0.0.1:8057/302', true, 301);
78
- }
79
- break;
80
-
81
- case '/301/bad-tld':
82
- header('Location: http://foo.example.', true, 301);
83
- break;
84
-
85
- case '/301/invalid':
86
- header('Location: //?foo=bar', true, 301);
87
- break;
88
-
89
- case '/302':
90
- if (!isset($vars['HTTP_AUTHORIZATION'])) {
91
- header('Location: http://localhost:8057/', true, 302);
92
- }
93
- break;
94
-
95
- case '/302/relative':
96
- header('Location: ..', true, 302);
97
- break;
98
-
99
- case '/304':
100
- header('Content-Length: 10', true, 304);
101
- echo '12345';
102
-
103
- return;
104
-
105
- case '/307':
106
- header('Location: http://localhost:8057/post', true, 307);
107
- break;
108
-
109
- case '/length-broken':
110
- header('Content-Length: 1000');
111
- break;
112
-
113
- case '/post':
114
- $output = json_encode($_POST + ['REQUEST_METHOD' => $vars['REQUEST_METHOD']], \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE);
115
- header('Content-Type: application/json', true);
116
- header('Content-Length: '.strlen($output));
117
- echo $output;
118
- exit;
119
-
120
- case '/timeout-header':
121
- usleep(300000);
122
- break;
123
-
124
- case '/timeout-body':
125
- echo '<1>';
126
- @ob_flush();
127
- flush();
128
- usleep(500000);
129
- echo '<2>';
130
- exit;
131
-
132
- case '/timeout-long':
133
- ignore_user_abort(false);
134
- sleep(1);
135
- while (true) {
136
- echo '<1>';
137
- @ob_flush();
138
- flush();
139
- usleep(500);
140
- }
141
- exit;
142
-
143
- case '/chunked':
144
- header('Transfer-Encoding: chunked');
145
- echo "8\r\nSymfony \r\n5\r\nis aw\r\n6\r\nesome!\r\n0\r\n\r\n";
146
- exit;
147
-
148
- case '/chunked-broken':
149
- header('Transfer-Encoding: chunked');
150
- echo "8\r\nSymfony \r\n5\r\nis aw\r\n6\r\ne";
151
- exit;
152
-
153
- case '/gzip-broken':
154
- header('Content-Encoding: gzip');
155
- echo str_repeat('-', 1000);
156
- exit;
157
-
158
- case '/max-duration':
159
- ignore_user_abort(false);
160
- while (true) {
161
- echo '<1>';
162
- @ob_flush();
163
- flush();
164
- usleep(500);
165
- }
166
- exit;
167
-
168
- case '/json':
169
- header('Content-Type: application/json');
170
- echo json_encode([
171
- 'documents' => [
172
- ['id' => '/json/1'],
173
- ['id' => '/json/2'],
174
- ['id' => '/json/3'],
175
- ],
176
- ]);
177
- exit;
178
-
179
- case '/json/1':
180
- case '/json/2':
181
- case '/json/3':
182
- header('Content-Type: application/json');
183
- echo json_encode([
184
- 'title' => $vars['REQUEST_URI'],
185
- ]);
186
-
187
- exit;
188
- }
189
-
190
- header('Content-Type: application/json', true);
191
-
192
- echo $json;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Test/HttpClientTestCase.php DELETED
@@ -1,1132 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Test;
13
-
14
- use PHPUnit\Framework\TestCase;
15
- use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
16
- use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
17
- use Symfony\Contracts\HttpClient\Exception\TimeoutExceptionInterface;
18
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
19
- use Symfony\Contracts\HttpClient\HttpClientInterface;
20
-
21
- /**
22
- * A reference test suite for HttpClientInterface implementations.
23
- */
24
- abstract class HttpClientTestCase extends TestCase
25
- {
26
- public static function setUpBeforeClass(): void
27
- {
28
- TestHttpServer::start();
29
- }
30
-
31
- abstract protected function getHttpClient(string $testCase): HttpClientInterface;
32
-
33
- public function testGetRequest()
34
- {
35
- $client = $this->getHttpClient(__FUNCTION__);
36
- $response = $client->request('GET', 'http://localhost:8057', [
37
- 'headers' => ['Foo' => 'baR'],
38
- 'user_data' => $data = new \stdClass(),
39
- ]);
40
-
41
- $this->assertSame([], $response->getInfo('response_headers'));
42
- $this->assertSame($data, $response->getInfo()['user_data']);
43
- $this->assertSame(200, $response->getStatusCode());
44
-
45
- $info = $response->getInfo();
46
- $this->assertNull($info['error']);
47
- $this->assertSame(0, $info['redirect_count']);
48
- $this->assertSame('HTTP/1.1 200 OK', $info['response_headers'][0]);
49
- $this->assertSame('Host: localhost:8057', $info['response_headers'][1]);
50
- $this->assertSame('http://localhost:8057/', $info['url']);
51
-
52
- $headers = $response->getHeaders();
53
-
54
- $this->assertSame('localhost:8057', $headers['host'][0]);
55
- $this->assertSame(['application/json'], $headers['content-type']);
56
-
57
- $body = json_decode($response->getContent(), true);
58
- $this->assertSame($body, $response->toArray());
59
-
60
- $this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']);
61
- $this->assertSame('/', $body['REQUEST_URI']);
62
- $this->assertSame('GET', $body['REQUEST_METHOD']);
63
- $this->assertSame('localhost:8057', $body['HTTP_HOST']);
64
- $this->assertSame('baR', $body['HTTP_FOO']);
65
-
66
- $response = $client->request('GET', 'http://localhost:8057/length-broken');
67
-
68
- $this->expectException(TransportExceptionInterface::class);
69
- $response->getContent();
70
- }
71
-
72
- public function testHeadRequest()
73
- {
74
- $client = $this->getHttpClient(__FUNCTION__);
75
- $response = $client->request('HEAD', 'http://localhost:8057/head', [
76
- 'headers' => ['Foo' => 'baR'],
77
- 'user_data' => $data = new \stdClass(),
78
- 'buffer' => false,
79
- ]);
80
-
81
- $this->assertSame([], $response->getInfo('response_headers'));
82
- $this->assertSame(200, $response->getStatusCode());
83
-
84
- $info = $response->getInfo();
85
- $this->assertSame('HTTP/1.1 200 OK', $info['response_headers'][0]);
86
- $this->assertSame('Host: localhost:8057', $info['response_headers'][1]);
87
-
88
- $headers = $response->getHeaders();
89
-
90
- $this->assertSame('localhost:8057', $headers['host'][0]);
91
- $this->assertSame(['application/json'], $headers['content-type']);
92
- $this->assertTrue(0 < $headers['content-length'][0]);
93
-
94
- $this->assertSame('', $response->getContent());
95
- }
96
-
97
- public function testNonBufferedGetRequest()
98
- {
99
- $client = $this->getHttpClient(__FUNCTION__);
100
- $response = $client->request('GET', 'http://localhost:8057', [
101
- 'buffer' => false,
102
- 'headers' => ['Foo' => 'baR'],
103
- ]);
104
-
105
- $body = $response->toArray();
106
- $this->assertSame('baR', $body['HTTP_FOO']);
107
-
108
- $this->expectException(TransportExceptionInterface::class);
109
- $response->getContent();
110
- }
111
-
112
- public function testBufferSink()
113
- {
114
- $sink = fopen('php://temp', 'w+');
115
- $client = $this->getHttpClient(__FUNCTION__);
116
- $response = $client->request('GET', 'http://localhost:8057', [
117
- 'buffer' => $sink,
118
- 'headers' => ['Foo' => 'baR'],
119
- ]);
120
-
121
- $body = $response->toArray();
122
- $this->assertSame('baR', $body['HTTP_FOO']);
123
-
124
- rewind($sink);
125
- $sink = stream_get_contents($sink);
126
- $this->assertSame($sink, $response->getContent());
127
- }
128
-
129
- public function testConditionalBuffering()
130
- {
131
- $client = $this->getHttpClient(__FUNCTION__);
132
- $response = $client->request('GET', 'http://localhost:8057');
133
- $firstContent = $response->getContent();
134
- $secondContent = $response->getContent();
135
-
136
- $this->assertSame($firstContent, $secondContent);
137
-
138
- $response = $client->request('GET', 'http://localhost:8057', ['buffer' => function () { return false; }]);
139
- $response->getContent();
140
-
141
- $this->expectException(TransportExceptionInterface::class);
142
- $response->getContent();
143
- }
144
-
145
- public function testReentrantBufferCallback()
146
- {
147
- $client = $this->getHttpClient(__FUNCTION__);
148
-
149
- $response = $client->request('GET', 'http://localhost:8057', ['buffer' => function () use (&$response) {
150
- $response->cancel();
151
-
152
- return true;
153
- }]);
154
-
155
- $this->assertSame(200, $response->getStatusCode());
156
-
157
- $this->expectException(TransportExceptionInterface::class);
158
- $response->getContent();
159
- }
160
-
161
- public function testThrowingBufferCallback()
162
- {
163
- $client = $this->getHttpClient(__FUNCTION__);
164
-
165
- $response = $client->request('GET', 'http://localhost:8057', ['buffer' => function () {
166
- throw new \Exception('Boo.');
167
- }]);
168
-
169
- $this->assertSame(200, $response->getStatusCode());
170
-
171
- $this->expectException(TransportExceptionInterface::class);
172
- $this->expectExceptionMessage('Boo');
173
- $response->getContent();
174
- }
175
-
176
- public function testUnsupportedOption()
177
- {
178
- $client = $this->getHttpClient(__FUNCTION__);
179
-
180
- $this->expectException(\InvalidArgumentException::class);
181
- $client->request('GET', 'http://localhost:8057', [
182
- 'capture_peer_cert' => 1.0,
183
- ]);
184
- }
185
-
186
- public function testHttpVersion()
187
- {
188
- $client = $this->getHttpClient(__FUNCTION__);
189
- $response = $client->request('GET', 'http://localhost:8057', [
190
- 'http_version' => 1.0,
191
- ]);
192
-
193
- $this->assertSame(200, $response->getStatusCode());
194
- $this->assertSame('HTTP/1.0 200 OK', $response->getInfo('response_headers')[0]);
195
-
196
- $body = $response->toArray();
197
-
198
- $this->assertSame('HTTP/1.0', $body['SERVER_PROTOCOL']);
199
- $this->assertSame('GET', $body['REQUEST_METHOD']);
200
- $this->assertSame('/', $body['REQUEST_URI']);
201
- }
202
-
203
- public function testChunkedEncoding()
204
- {
205
- $client = $this->getHttpClient(__FUNCTION__);
206
- $response = $client->request('GET', 'http://localhost:8057/chunked');
207
-
208
- $this->assertSame(['chunked'], $response->getHeaders()['transfer-encoding']);
209
- $this->assertSame('Symfony is awesome!', $response->getContent());
210
-
211
- $response = $client->request('GET', 'http://localhost:8057/chunked-broken');
212
-
213
- $this->expectException(TransportExceptionInterface::class);
214
- $response->getContent();
215
- }
216
-
217
- public function testClientError()
218
- {
219
- $client = $this->getHttpClient(__FUNCTION__);
220
- $response = $client->request('GET', 'http://localhost:8057/404');
221
-
222
- $client->stream($response)->valid();
223
-
224
- $this->assertSame(404, $response->getInfo('http_code'));
225
-
226
- try {
227
- $response->getHeaders();
228
- $this->fail(ClientExceptionInterface::class.' expected');
229
- } catch (ClientExceptionInterface $e) {
230
- }
231
-
232
- try {
233
- $response->getContent();
234
- $this->fail(ClientExceptionInterface::class.' expected');
235
- } catch (ClientExceptionInterface $e) {
236
- }
237
-
238
- $this->assertSame(404, $response->getStatusCode());
239
- $this->assertSame(['application/json'], $response->getHeaders(false)['content-type']);
240
- $this->assertNotEmpty($response->getContent(false));
241
-
242
- $response = $client->request('GET', 'http://localhost:8057/404');
243
-
244
- try {
245
- foreach ($client->stream($response) as $chunk) {
246
- $this->assertTrue($chunk->isFirst());
247
- }
248
- $this->fail(ClientExceptionInterface::class.' expected');
249
- } catch (ClientExceptionInterface $e) {
250
- }
251
- }
252
-
253
- public function testIgnoreErrors()
254
- {
255
- $client = $this->getHttpClient(__FUNCTION__);
256
- $response = $client->request('GET', 'http://localhost:8057/404');
257
-
258
- $this->assertSame(404, $response->getStatusCode());
259
- }
260
-
261
- public function testDnsError()
262
- {
263
- $client = $this->getHttpClient(__FUNCTION__);
264
- $response = $client->request('GET', 'http://localhost:8057/301/bad-tld');
265
-
266
- try {
267
- $response->getStatusCode();
268
- $this->fail(TransportExceptionInterface::class.' expected');
269
- } catch (TransportExceptionInterface $e) {
270
- $this->addToAssertionCount(1);
271
- }
272
-
273
- try {
274
- $response->getStatusCode();
275
- $this->fail(TransportExceptionInterface::class.' still expected');
276
- } catch (TransportExceptionInterface $e) {
277
- $this->addToAssertionCount(1);
278
- }
279
-
280
- $response = $client->request('GET', 'http://localhost:8057/301/bad-tld');
281
-
282
- try {
283
- foreach ($client->stream($response) as $r => $chunk) {
284
- }
285
- $this->fail(TransportExceptionInterface::class.' expected');
286
- } catch (TransportExceptionInterface $e) {
287
- $this->addToAssertionCount(1);
288
- }
289
-
290
- $this->assertSame($response, $r);
291
- $this->assertNotNull($chunk->getError());
292
-
293
- $this->expectException(TransportExceptionInterface::class);
294
- foreach ($client->stream($response) as $chunk) {
295
- }
296
- }
297
-
298
- public function testInlineAuth()
299
- {
300
- $client = $this->getHttpClient(__FUNCTION__);
301
- $response = $client->request('GET', 'http://foo:bar%3Dbar@localhost:8057');
302
-
303
- $body = $response->toArray();
304
-
305
- $this->assertSame('foo', $body['PHP_AUTH_USER']);
306
- $this->assertSame('bar=bar', $body['PHP_AUTH_PW']);
307
- }
308
-
309
- public function testBadRequestBody()
310
- {
311
- $client = $this->getHttpClient(__FUNCTION__);
312
-
313
- $this->expectException(TransportExceptionInterface::class);
314
-
315
- $response = $client->request('POST', 'http://localhost:8057/', [
316
- 'body' => function () { yield []; },
317
- ]);
318
-
319
- $response->getStatusCode();
320
- }
321
-
322
- public function test304()
323
- {
324
- $client = $this->getHttpClient(__FUNCTION__);
325
- $response = $client->request('GET', 'http://localhost:8057/304', [
326
- 'headers' => ['If-Match' => '"abc"'],
327
- 'buffer' => false,
328
- ]);
329
-
330
- $this->assertSame(304, $response->getStatusCode());
331
- $this->assertSame('', $response->getContent(false));
332
- }
333
-
334
- public function testRedirects()
335
- {
336
- $client = $this->getHttpClient(__FUNCTION__);
337
- $response = $client->request('POST', 'http://localhost:8057/301', [
338
- 'auth_basic' => 'foo:bar',
339
- 'body' => function () {
340
- yield 'foo=bar';
341
- },
342
- ]);
343
-
344
- $body = $response->toArray();
345
- $this->assertSame('GET', $body['REQUEST_METHOD']);
346
- $this->assertSame('Basic Zm9vOmJhcg==', $body['HTTP_AUTHORIZATION']);
347
- $this->assertSame('http://localhost:8057/', $response->getInfo('url'));
348
-
349
- $this->assertSame(2, $response->getInfo('redirect_count'));
350
- $this->assertNull($response->getInfo('redirect_url'));
351
-
352
- $expected = [
353
- 'HTTP/1.1 301 Moved Permanently',
354
- 'Location: http://127.0.0.1:8057/302',
355
- 'Content-Type: application/json',
356
- 'HTTP/1.1 302 Found',
357
- 'Location: http://localhost:8057/',
358
- 'Content-Type: application/json',
359
- 'HTTP/1.1 200 OK',
360
- 'Content-Type: application/json',
361
- ];
362
-
363
- $filteredHeaders = array_values(array_filter($response->getInfo('response_headers'), function ($h) {
364
- return \in_array(substr($h, 0, 4), ['HTTP', 'Loca', 'Cont'], true) && 'Content-Encoding: gzip' !== $h;
365
- }));
366
-
367
- $this->assertSame($expected, $filteredHeaders);
368
- }
369
-
370
- public function testInvalidRedirect()
371
- {
372
- $client = $this->getHttpClient(__FUNCTION__);
373
- $response = $client->request('GET', 'http://localhost:8057/301/invalid');
374
-
375
- $this->assertSame(301, $response->getStatusCode());
376
- $this->assertSame(['//?foo=bar'], $response->getHeaders(false)['location']);
377
- $this->assertSame(0, $response->getInfo('redirect_count'));
378
- $this->assertNull($response->getInfo('redirect_url'));
379
-
380
- $this->expectException(RedirectionExceptionInterface::class);
381
- $response->getHeaders();
382
- }
383
-
384
- public function testRelativeRedirects()
385
- {
386
- $client = $this->getHttpClient(__FUNCTION__);
387
- $response = $client->request('GET', 'http://localhost:8057/302/relative');
388
-
389
- $body = $response->toArray();
390
-
391
- $this->assertSame('/', $body['REQUEST_URI']);
392
- $this->assertNull($response->getInfo('redirect_url'));
393
-
394
- $response = $client->request('GET', 'http://localhost:8057/302/relative', [
395
- 'max_redirects' => 0,
396
- ]);
397
-
398
- $this->assertSame(302, $response->getStatusCode());
399
- $this->assertSame('http://localhost:8057/', $response->getInfo('redirect_url'));
400
- }
401
-
402
- public function testRedirect307()
403
- {
404
- $client = $this->getHttpClient(__FUNCTION__);
405
-
406
- $response = $client->request('POST', 'http://localhost:8057/307', [
407
- 'body' => function () {
408
- yield 'foo=bar';
409
- },
410
- 'max_redirects' => 0,
411
- ]);
412
-
413
- $this->assertSame(307, $response->getStatusCode());
414
-
415
- $response = $client->request('POST', 'http://localhost:8057/307', [
416
- 'body' => 'foo=bar',
417
- ]);
418
-
419
- $body = $response->toArray();
420
-
421
- $this->assertSame(['foo' => 'bar', 'REQUEST_METHOD' => 'POST'], $body);
422
- }
423
-
424
- public function testMaxRedirects()
425
- {
426
- $client = $this->getHttpClient(__FUNCTION__);
427
- $response = $client->request('GET', 'http://localhost:8057/301', [
428
- 'max_redirects' => 1,
429
- 'auth_basic' => 'foo:bar',
430
- ]);
431
-
432
- try {
433
- $response->getHeaders();
434
- $this->fail(RedirectionExceptionInterface::class.' expected');
435
- } catch (RedirectionExceptionInterface $e) {
436
- }
437
-
438
- $this->assertSame(302, $response->getStatusCode());
439
- $this->assertSame(1, $response->getInfo('redirect_count'));
440
- $this->assertSame('http://localhost:8057/', $response->getInfo('redirect_url'));
441
-
442
- $expected = [
443
- 'HTTP/1.1 301 Moved Permanently',
444
- 'Location: http://127.0.0.1:8057/302',
445
- 'Content-Type: application/json',
446
- 'HTTP/1.1 302 Found',
447
- 'Location: http://localhost:8057/',
448
- 'Content-Type: application/json',
449
- ];
450
-
451
- $filteredHeaders = array_values(array_filter($response->getInfo('response_headers'), function ($h) {
452
- return \in_array(substr($h, 0, 4), ['HTTP', 'Loca', 'Cont'], true);
453
- }));
454
-
455
- $this->assertSame($expected, $filteredHeaders);
456
- }
457
-
458
- public function testStream()
459
- {
460
- $client = $this->getHttpClient(__FUNCTION__);
461
-
462
- $response = $client->request('GET', 'http://localhost:8057');
463
- $chunks = $client->stream($response);
464
- $result = [];
465
-
466
- foreach ($chunks as $r => $chunk) {
467
- if ($chunk->isTimeout()) {
468
- $result[] = 't';
469
- } elseif ($chunk->isLast()) {
470
- $result[] = 'l';
471
- } elseif ($chunk->isFirst()) {
472
- $result[] = 'f';
473
- }
474
- }
475
-
476
- $this->assertSame($response, $r);
477
- $this->assertSame(['f', 'l'], $result);
478
-
479
- $chunk = null;
480
- $i = 0;
481
-
482
- foreach ($client->stream($response) as $chunk) {
483
- ++$i;
484
- }
485
-
486
- $this->assertSame(1, $i);
487
- $this->assertTrue($chunk->isLast());
488
- }
489
-
490
- public function testAddToStream()
491
- {
492
- $client = $this->getHttpClient(__FUNCTION__);
493
-
494
- $r1 = $client->request('GET', 'http://localhost:8057');
495
-
496
- $completed = [];
497
-
498
- $pool = [$r1];
499
-
500
- while ($pool) {
501
- $chunks = $client->stream($pool);
502
- $pool = [];
503
-
504
- foreach ($chunks as $r => $chunk) {
505
- if (!$chunk->isLast()) {
506
- continue;
507
- }
508
-
509
- if ($r1 === $r) {
510
- $r2 = $client->request('GET', 'http://localhost:8057');
511
- $pool[] = $r2;
512
- }
513
-
514
- $completed[] = $r;
515
- }
516
- }
517
-
518
- $this->assertSame([$r1, $r2], $completed);
519
- }
520
-
521
- public function testCompleteTypeError()
522
- {
523
- $client = $this->getHttpClient(__FUNCTION__);
524
-
525
- $this->expectException(\TypeError::class);
526
- $client->stream(123);
527
- }
528
-
529
- public function testOnProgress()
530
- {
531
- $client = $this->getHttpClient(__FUNCTION__);
532
- $response = $client->request('POST', 'http://localhost:8057/post', [
533
- 'headers' => ['Content-Length' => 14],
534
- 'body' => 'foo=0123456789',
535
- 'on_progress' => function (...$state) use (&$steps) { $steps[] = $state; },
536
- ]);
537
-
538
- $body = $response->toArray();
539
-
540
- $this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $body);
541
- $this->assertSame([0, 0], \array_slice($steps[0], 0, 2));
542
- $lastStep = \array_slice($steps, -1)[0];
543
- $this->assertSame([57, 57], \array_slice($lastStep, 0, 2));
544
- $this->assertSame('http://localhost:8057/post', $steps[0][2]['url']);
545
- }
546
-
547
- public function testPostJson()
548
- {
549
- $client = $this->getHttpClient(__FUNCTION__);
550
-
551
- $response = $client->request('POST', 'http://localhost:8057/post', [
552
- 'json' => ['foo' => 'bar'],
553
- ]);
554
-
555
- $body = $response->toArray();
556
-
557
- $this->assertStringContainsString('json', $body['content-type']);
558
- unset($body['content-type']);
559
- $this->assertSame(['foo' => 'bar', 'REQUEST_METHOD' => 'POST'], $body);
560
- }
561
-
562
- public function testPostArray()
563
- {
564
- $client = $this->getHttpClient(__FUNCTION__);
565
-
566
- $response = $client->request('POST', 'http://localhost:8057/post', [
567
- 'body' => ['foo' => 'bar'],
568
- ]);
569
-
570
- $this->assertSame(['foo' => 'bar', 'REQUEST_METHOD' => 'POST'], $response->toArray());
571
- }
572
-
573
- public function testPostResource()
574
- {
575
- $client = $this->getHttpClient(__FUNCTION__);
576
-
577
- $h = fopen('php://temp', 'w+');
578
- fwrite($h, 'foo=0123456789');
579
- rewind($h);
580
-
581
- $response = $client->request('POST', 'http://localhost:8057/post', [
582
- 'body' => $h,
583
- ]);
584
-
585
- $body = $response->toArray();
586
-
587
- $this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $body);
588
- }
589
-
590
- public function testPostCallback()
591
- {
592
- $client = $this->getHttpClient(__FUNCTION__);
593
-
594
- $response = $client->request('POST', 'http://localhost:8057/post', [
595
- 'body' => function () {
596
- yield 'foo';
597
- yield '';
598
- yield '=';
599
- yield '0123456789';
600
- },
601
- ]);
602
-
603
- $this->assertSame(['foo' => '0123456789', 'REQUEST_METHOD' => 'POST'], $response->toArray());
604
- }
605
-
606
- public function testCancel()
607
- {
608
- $client = $this->getHttpClient(__FUNCTION__);
609
- $response = $client->request('GET', 'http://localhost:8057/timeout-header');
610
-
611
- $response->cancel();
612
- $this->expectException(TransportExceptionInterface::class);
613
- $response->getHeaders();
614
- }
615
-
616
- public function testInfoOnCanceledResponse()
617
- {
618
- $client = $this->getHttpClient(__FUNCTION__);
619
-
620
- $response = $client->request('GET', 'http://localhost:8057/timeout-header');
621
-
622
- $this->assertFalse($response->getInfo('canceled'));
623
- $response->cancel();
624
- $this->assertTrue($response->getInfo('canceled'));
625
- }
626
-
627
- public function testCancelInStream()
628
- {
629
- $client = $this->getHttpClient(__FUNCTION__);
630
- $response = $client->request('GET', 'http://localhost:8057/404');
631
-
632
- foreach ($client->stream($response) as $chunk) {
633
- $response->cancel();
634
- }
635
-
636
- $this->expectException(TransportExceptionInterface::class);
637
-
638
- foreach ($client->stream($response) as $chunk) {
639
- }
640
- }
641
-
642
- public function testOnProgressCancel()
643
- {
644
- $client = $this->getHttpClient(__FUNCTION__);
645
- $response = $client->request('GET', 'http://localhost:8057/timeout-body', [
646
- 'on_progress' => function ($dlNow) {
647
- if (0 < $dlNow) {
648
- throw new \Exception('Aborting the request.');
649
- }
650
- },
651
- ]);
652
-
653
- try {
654
- foreach ($client->stream([$response]) as $chunk) {
655
- }
656
- $this->fail(ClientExceptionInterface::class.' expected');
657
- } catch (TransportExceptionInterface $e) {
658
- $this->assertSame('Aborting the request.', $e->getPrevious()->getMessage());
659
- }
660
-
661
- $this->assertNotNull($response->getInfo('error'));
662
- $this->expectException(TransportExceptionInterface::class);
663
- $response->getContent();
664
- }
665
-
666
- public function testOnProgressError()
667
- {
668
- $client = $this->getHttpClient(__FUNCTION__);
669
- $response = $client->request('GET', 'http://localhost:8057/timeout-body', [
670
- 'on_progress' => function ($dlNow) {
671
- if (0 < $dlNow) {
672
- throw new \Error('BUG.');
673
- }
674
- },
675
- ]);
676
-
677
- try {
678
- foreach ($client->stream([$response]) as $chunk) {
679
- }
680
- $this->fail('Error expected');
681
- } catch (\Error $e) {
682
- $this->assertSame('BUG.', $e->getMessage());
683
- }
684
-
685
- $this->assertNotNull($response->getInfo('error'));
686
- $this->expectException(TransportExceptionInterface::class);
687
- $response->getContent();
688
- }
689
-
690
- public function testResolve()
691
- {
692
- $client = $this->getHttpClient(__FUNCTION__);
693
- $response = $client->request('GET', 'http://symfony.com:8057/', [
694
- 'resolve' => ['symfony.com' => '127.0.0.1'],
695
- ]);
696
-
697
- $this->assertSame(200, $response->getStatusCode());
698
- $this->assertSame(200, $client->request('GET', 'http://symfony.com:8057/')->getStatusCode());
699
-
700
- $response = null;
701
- $this->expectException(TransportExceptionInterface::class);
702
- $client->request('GET', 'http://symfony.com:8057/', ['timeout' => 1]);
703
- }
704
-
705
- public function testIdnResolve()
706
- {
707
- $client = $this->getHttpClient(__FUNCTION__);
708
-
709
- $response = $client->request('GET', 'http://0-------------------------------------------------------------0.com:8057/', [
710
- 'resolve' => ['0-------------------------------------------------------------0.com' => '127.0.0.1'],
711
- ]);
712
-
713
- $this->assertSame(200, $response->getStatusCode());
714
-
715
- $response = $client->request('GET', 'http://Bücher.example:8057/', [
716
- 'resolve' => ['xn--bcher-kva.example' => '127.0.0.1'],
717
- ]);
718
-
719
- $this->assertSame(200, $response->getStatusCode());
720
- }
721
-
722
- public function testNotATimeout()
723
- {
724
- $client = $this->getHttpClient(__FUNCTION__);
725
- $response = $client->request('GET', 'http://localhost:8057/timeout-header', [
726
- 'timeout' => 0.9,
727
- ]);
728
- sleep(1);
729
- $this->assertSame(200, $response->getStatusCode());
730
- }
731
-
732
- public function testTimeoutOnAccess()
733
- {
734
- $client = $this->getHttpClient(__FUNCTION__);
735
- $response = $client->request('GET', 'http://localhost:8057/timeout-header', [
736
- 'timeout' => 0.1,
737
- ]);
738
-
739
- $this->expectException(TransportExceptionInterface::class);
740
- $response->getHeaders();
741
- }
742
-
743
- public function testTimeoutIsNotAFatalError()
744
- {
745
- usleep(300000); // wait for the previous test to release the server
746
- $client = $this->getHttpClient(__FUNCTION__);
747
- $response = $client->request('GET', 'http://localhost:8057/timeout-body', [
748
- 'timeout' => 0.25,
749
- ]);
750
-
751
- try {
752
- $response->getContent();
753
- $this->fail(TimeoutExceptionInterface::class.' expected');
754
- } catch (TimeoutExceptionInterface $e) {
755
- }
756
-
757
- for ($i = 0; $i < 10; ++$i) {
758
- try {
759
- $this->assertSame('<1><2>', $response->getContent());
760
- break;
761
- } catch (TimeoutExceptionInterface $e) {
762
- }
763
- }
764
-
765
- if (10 === $i) {
766
- throw $e;
767
- }
768
- }
769
-
770
- public function testTimeoutOnStream()
771
- {
772
- $client = $this->getHttpClient(__FUNCTION__);
773
- $response = $client->request('GET', 'http://localhost:8057/timeout-body');
774
-
775
- $this->assertSame(200, $response->getStatusCode());
776
- $chunks = $client->stream([$response], 0.2);
777
-
778
- $result = [];
779
-
780
- foreach ($chunks as $r => $chunk) {
781
- if ($chunk->isTimeout()) {
782
- $result[] = 't';
783
- } else {
784
- $result[] = $chunk->getContent();
785
- }
786
- }
787
-
788
- $this->assertSame(['<1>', 't'], $result);
789
-
790
- $chunks = $client->stream([$response]);
791
-
792
- foreach ($chunks as $r => $chunk) {
793
- $this->assertSame('<2>', $chunk->getContent());
794
- $this->assertSame('<1><2>', $r->getContent());
795
-
796
- return;
797
- }
798
-
799
- $this->fail('The response should have completed');
800
- }
801
-
802
- public function testUncheckedTimeoutThrows()
803
- {
804
- $client = $this->getHttpClient(__FUNCTION__);
805
- $response = $client->request('GET', 'http://localhost:8057/timeout-body');
806
- $chunks = $client->stream([$response], 0.1);
807
-
808
- $this->expectException(TransportExceptionInterface::class);
809
-
810
- foreach ($chunks as $r => $chunk) {
811
- }
812
- }
813
-
814
- public function testTimeoutWithActiveConcurrentStream()
815
- {
816
- $p1 = TestHttpServer::start(8067);
817
- $p2 = TestHttpServer::start(8077);
818
-
819
- $client = $this->getHttpClient(__FUNCTION__);
820
- $streamingResponse = $client->request('GET', 'http://localhost:8067/max-duration');
821
- $blockingResponse = $client->request('GET', 'http://localhost:8077/timeout-body', [
822
- 'timeout' => 0.25,
823
- ]);
824
-
825
- $this->assertSame(200, $streamingResponse->getStatusCode());
826
- $this->assertSame(200, $blockingResponse->getStatusCode());
827
-
828
- $this->expectException(TransportExceptionInterface::class);
829
-
830
- try {
831
- $blockingResponse->getContent();
832
- } finally {
833
- $p1->stop();
834
- $p2->stop();
835
- }
836
- }
837
-
838
- public function testTimeoutOnInitialize()
839
- {
840
- $p1 = TestHttpServer::start(8067);
841
- $p2 = TestHttpServer::start(8077);
842
-
843
- $client = $this->getHttpClient(__FUNCTION__);
844
- $start = microtime(true);
845
- $responses = [];
846
-
847
- $responses[] = $client->request('GET', 'http://localhost:8067/timeout-header', ['timeout' => 0.25]);
848
- $responses[] = $client->request('GET', 'http://localhost:8077/timeout-header', ['timeout' => 0.25]);
849
- $responses[] = $client->request('GET', 'http://localhost:8067/timeout-header', ['timeout' => 0.25]);
850
- $responses[] = $client->request('GET', 'http://localhost:8077/timeout-header', ['timeout' => 0.25]);
851
-
852
- try {
853
- foreach ($responses as $response) {
854
- try {
855
- $response->getContent();
856
- $this->fail(TransportExceptionInterface::class.' expected');
857
- } catch (TransportExceptionInterface $e) {
858
- }
859
- }
860
- $responses = [];
861
-
862
- $duration = microtime(true) - $start;
863
-
864
- $this->assertLessThan(1.0, $duration);
865
- } finally {
866
- $p1->stop();
867
- $p2->stop();
868
- }
869
- }
870
-
871
- public function testTimeoutOnDestruct()
872
- {
873
- $p1 = TestHttpServer::start(8067);
874
- $p2 = TestHttpServer::start(8077);
875
-
876
- $client = $this->getHttpClient(__FUNCTION__);
877
- $start = microtime(true);
878
- $responses = [];
879
-
880
- $responses[] = $client->request('GET', 'http://localhost:8067/timeout-header', ['timeout' => 0.25]);
881
- $responses[] = $client->request('GET', 'http://localhost:8077/timeout-header', ['timeout' => 0.25]);
882
- $responses[] = $client->request('GET', 'http://localhost:8067/timeout-header', ['timeout' => 0.25]);
883
- $responses[] = $client->request('GET', 'http://localhost:8077/timeout-header', ['timeout' => 0.25]);
884
-
885
- try {
886
- while ($response = array_shift($responses)) {
887
- try {
888
- unset($response);
889
- $this->fail(TransportExceptionInterface::class.' expected');
890
- } catch (TransportExceptionInterface $e) {
891
- }
892
- }
893
-
894
- $duration = microtime(true) - $start;
895
-
896
- $this->assertLessThan(1.0, $duration);
897
- } finally {
898
- $p1->stop();
899
- $p2->stop();
900
- }
901
- }
902
-
903
- public function testDestruct()
904
- {
905
- $client = $this->getHttpClient(__FUNCTION__);
906
-
907
- $start = microtime(true);
908
- $client->request('GET', 'http://localhost:8057/timeout-long');
909
- $client = null;
910
- $duration = microtime(true) - $start;
911
-
912
- $this->assertGreaterThan(1, $duration);
913
- $this->assertLessThan(4, $duration);
914
- }
915
-
916
- public function testGetContentAfterDestruct()
917
- {
918
- $client = $this->getHttpClient(__FUNCTION__);
919
-
920
- try {
921
- $client->request('GET', 'http://localhost:8057/404');
922
- $this->fail(ClientExceptionInterface::class.' expected');
923
- } catch (ClientExceptionInterface $e) {
924
- $this->assertSame('GET', $e->getResponse()->toArray(false)['REQUEST_METHOD']);
925
- }
926
- }
927
-
928
- public function testGetEncodedContentAfterDestruct()
929
- {
930
- $client = $this->getHttpClient(__FUNCTION__);
931
-
932
- try {
933
- $client->request('GET', 'http://localhost:8057/404-gzipped');
934
- $this->fail(ClientExceptionInterface::class.' expected');
935
- } catch (ClientExceptionInterface $e) {
936
- $this->assertSame('some text', $e->getResponse()->getContent(false));
937
- }
938
- }
939
-
940
- public function testProxy()
941
- {
942
- $client = $this->getHttpClient(__FUNCTION__);
943
- $response = $client->request('GET', 'http://localhost:8057/', [
944
- 'proxy' => 'http://localhost:8057',
945
- ]);
946
-
947
- $body = $response->toArray();
948
- $this->assertSame('localhost:8057', $body['HTTP_HOST']);
949
- $this->assertMatchesRegularExpression('#^http://(localhost|127\.0\.0\.1):8057/$#', $body['REQUEST_URI']);
950
-
951
- $response = $client->request('GET', 'http://localhost:8057/', [
952
- 'proxy' => 'http://foo:b%3Dar@localhost:8057',
953
- ]);
954
-
955
- $body = $response->toArray();
956
- $this->assertSame('Basic Zm9vOmI9YXI=', $body['HTTP_PROXY_AUTHORIZATION']);
957
-
958
- $_SERVER['http_proxy'] = 'http://localhost:8057';
959
- try {
960
- $response = $client->request('GET', 'http://localhost:8057/');
961
- $body = $response->toArray();
962
- $this->assertSame('localhost:8057', $body['HTTP_HOST']);
963
- $this->assertMatchesRegularExpression('#^http://(localhost|127\.0\.0\.1):8057/$#', $body['REQUEST_URI']);
964
- } finally {
965
- unset($_SERVER['http_proxy']);
966
- }
967
- }
968
-
969
- public function testNoProxy()
970
- {
971
- putenv('no_proxy='.$_SERVER['no_proxy'] = 'example.com, localhost');
972
-
973
- try {
974
- $client = $this->getHttpClient(__FUNCTION__);
975
- $response = $client->request('GET', 'http://localhost:8057/', [
976
- 'proxy' => 'http://localhost:8057',
977
- ]);
978
-
979
- $body = $response->toArray();
980
-
981
- $this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']);
982
- $this->assertSame('/', $body['REQUEST_URI']);
983
- $this->assertSame('GET', $body['REQUEST_METHOD']);
984
- } finally {
985
- putenv('no_proxy');
986
- unset($_SERVER['no_proxy']);
987
- }
988
- }
989
-
990
- /**
991
- * @requires extension zlib
992
- */
993
- public function testAutoEncodingRequest()
994
- {
995
- $client = $this->getHttpClient(__FUNCTION__);
996
- $response = $client->request('GET', 'http://localhost:8057');
997
-
998
- $this->assertSame(200, $response->getStatusCode());
999
-
1000
- $headers = $response->getHeaders();
1001
-
1002
- $this->assertSame(['Accept-Encoding'], $headers['vary']);
1003
- $this->assertStringContainsString('gzip', $headers['content-encoding'][0]);
1004
-
1005
- $body = $response->toArray();
1006
-
1007
- $this->assertStringContainsString('gzip', $body['HTTP_ACCEPT_ENCODING']);
1008
- }
1009
-
1010
- public function testBaseUri()
1011
- {
1012
- $client = $this->getHttpClient(__FUNCTION__);
1013
- $response = $client->request('GET', '../404', [
1014
- 'base_uri' => 'http://localhost:8057/abc/',
1015
- ]);
1016
-
1017
- $this->assertSame(404, $response->getStatusCode());
1018
- $this->assertSame(['application/json'], $response->getHeaders(false)['content-type']);
1019
- }
1020
-
1021
- public function testQuery()
1022
- {
1023
- $client = $this->getHttpClient(__FUNCTION__);
1024
- $response = $client->request('GET', 'http://localhost:8057/?a=a', [
1025
- 'query' => ['b' => 'b'],
1026
- ]);
1027
-
1028
- $body = $response->toArray();
1029
- $this->assertSame('GET', $body['REQUEST_METHOD']);
1030
- $this->assertSame('/?a=a&b=b', $body['REQUEST_URI']);
1031
- }
1032
-
1033
- public function testInformationalResponse()
1034
- {
1035
- $client = $this->getHttpClient(__FUNCTION__);
1036
- $response = $client->request('GET', 'http://localhost:8057/103');
1037
-
1038
- $this->assertSame('Here the body', $response->getContent());
1039
- $this->assertSame(200, $response->getStatusCode());
1040
- }
1041
-
1042
- public function testInformationalResponseStream()
1043
- {
1044
- $client = $this->getHttpClient(__FUNCTION__);
1045
- $response = $client->request('GET', 'http://localhost:8057/103');
1046
-
1047
- $chunks = [];
1048
- foreach ($client->stream($response) as $chunk) {
1049
- $chunks[] = $chunk;
1050
- }
1051
-
1052
- $this->assertSame(103, $chunks[0]->getInformationalStatus()[0]);
1053
- $this->assertSame(['</style.css>; rel=preload; as=style', '</script.js>; rel=preload; as=script'], $chunks[0]->getInformationalStatus()[1]['link']);
1054
- $this->assertTrue($chunks[1]->isFirst());
1055
- $this->assertSame('Here the body', $chunks[2]->getContent());
1056
- $this->assertTrue($chunks[3]->isLast());
1057
- $this->assertNull($chunks[3]->getInformationalStatus());
1058
-
1059
- $this->assertSame(['date', 'content-length'], array_keys($response->getHeaders()));
1060
- $this->assertContains('Link: </style.css>; rel=preload; as=style', $response->getInfo('response_headers'));
1061
- }
1062
-
1063
- /**
1064
- * @requires extension zlib
1065
- */
1066
- public function testUserlandEncodingRequest()
1067
- {
1068
- $client = $this->getHttpClient(__FUNCTION__);
1069
- $response = $client->request('GET', 'http://localhost:8057', [
1070
- 'headers' => ['Accept-Encoding' => 'gzip'],
1071
- ]);
1072
-
1073
- $headers = $response->getHeaders();
1074
-
1075
- $this->assertSame(['Accept-Encoding'], $headers['vary']);
1076
- $this->assertStringContainsString('gzip', $headers['content-encoding'][0]);
1077
-
1078
- $body = $response->getContent();
1079
- $this->assertSame("\x1F", $body[0]);
1080
-
1081
- $body = json_decode(gzdecode($body), true);
1082
- $this->assertSame('gzip', $body['HTTP_ACCEPT_ENCODING']);
1083
- }
1084
-
1085
- /**
1086
- * @requires extension zlib
1087
- */
1088
- public function testGzipBroken()
1089
- {
1090
- $client = $this->getHttpClient(__FUNCTION__);
1091
- $response = $client->request('GET', 'http://localhost:8057/gzip-broken');
1092
-
1093
- $this->expectException(TransportExceptionInterface::class);
1094
- $response->getContent();
1095
- }
1096
-
1097
- public function testMaxDuration()
1098
- {
1099
- $client = $this->getHttpClient(__FUNCTION__);
1100
- $response = $client->request('GET', 'http://localhost:8057/max-duration', [
1101
- 'max_duration' => 0.1,
1102
- ]);
1103
-
1104
- $start = microtime(true);
1105
-
1106
- try {
1107
- $response->getContent();
1108
- } catch (TransportExceptionInterface $e) {
1109
- $this->addToAssertionCount(1);
1110
- }
1111
-
1112
- $duration = microtime(true) - $start;
1113
-
1114
- $this->assertLessThan(10, $duration);
1115
- }
1116
-
1117
- public function testWithOptions()
1118
- {
1119
- $client = $this->getHttpClient(__FUNCTION__);
1120
- if (!method_exists($client, 'withOptions')) {
1121
- $this->markTestSkipped(sprintf('Not implementing "%s::withOptions()" is deprecated.', get_debug_type($client)));
1122
- }
1123
-
1124
- $client2 = $client->withOptions(['base_uri' => 'http://localhost:8057/']);
1125
-
1126
- $this->assertNotSame($client, $client2);
1127
- $this->assertSame(\get_class($client), \get_class($client2));
1128
-
1129
- $response = $client2->request('GET', '/');
1130
- $this->assertSame(200, $response->getStatusCode());
1131
- }
1132
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/Test/TestHttpServer.php DELETED
@@ -1,46 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\HttpClient\Test;
13
-
14
- use Symfony\Component\Process\PhpExecutableFinder;
15
- use Symfony\Component\Process\Process;
16
-
17
- class TestHttpServer
18
- {
19
- private static $process = [];
20
-
21
- /**
22
- * @return Process
23
- */
24
- public static function start(int $port = 8057)
25
- {
26
- if (isset(self::$process[$port])) {
27
- self::$process[$port]->stop();
28
- } else {
29
- register_shutdown_function(static function () use ($port) {
30
- self::$process[$port]->stop();
31
- });
32
- }
33
-
34
- $finder = new PhpExecutableFinder();
35
- $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', '127.0.0.1:'.$port]));
36
- $process->setWorkingDirectory(__DIR__.'/Fixtures/web');
37
- $process->start();
38
- self::$process[$port] = $process;
39
-
40
- do {
41
- usleep(50000);
42
- } while (!@fopen('http://127.0.0.1:'.$port, 'r'));
43
-
44
- return $process;
45
- }
46
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client-contracts/composer.json DELETED
@@ -1,37 +0,0 @@
1
- {
2
- "name": "symfony/http-client-contracts",
3
- "type": "library",
4
- "description": "Generic abstractions related to HTTP clients",
5
- "keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
6
- "homepage": "https://symfony.com",
7
- "license": "MIT",
8
- "authors": [
9
- {
10
- "name": "Nicolas Grekas",
11
- "email": "p@tchwork.com"
12
- },
13
- {
14
- "name": "Symfony Community",
15
- "homepage": "https://symfony.com/contributors"
16
- }
17
- ],
18
- "require": {
19
- "php": ">=7.2.5"
20
- },
21
- "suggest": {
22
- "symfony/http-client-implementation": ""
23
- },
24
- "autoload": {
25
- "psr-4": { "Symfony\\Contracts\\HttpClient\\": "" }
26
- },
27
- "minimum-stability": "dev",
28
- "extra": {
29
- "branch-alias": {
30
- "dev-main": "2.5-dev"
31
- },
32
- "thanks": {
33
- "name": "symfony/contracts",
34
- "url": "https://github.com/symfony/contracts"
35
- }
36
- }
37
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/AmpHttpClient.php DELETED
@@ -1,176 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Amp\CancelledException;
15
- use Amp\Http\Client\DelegateHttpClient;
16
- use Amp\Http\Client\InterceptedHttpClient;
17
- use Amp\Http\Client\PooledHttpClient;
18
- use Amp\Http\Client\Request;
19
- use Amp\Http\Tunnel\Http1TunnelConnector;
20
- use Psr\Log\LoggerAwareInterface;
21
- use Psr\Log\LoggerAwareTrait;
22
- use Symfony\Component\HttpClient\Exception\TransportException;
23
- use Symfony\Component\HttpClient\Internal\AmpClientState;
24
- use Symfony\Component\HttpClient\Response\AmpResponse;
25
- use Symfony\Component\HttpClient\Response\ResponseStream;
26
- use Symfony\Contracts\HttpClient\HttpClientInterface;
27
- use Symfony\Contracts\HttpClient\ResponseInterface;
28
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
29
- use Symfony\Contracts\Service\ResetInterface;
30
-
31
- if (!interface_exists(DelegateHttpClient::class)) {
32
- throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the "amphp/http-client" package is not installed. Try running "composer require amphp/http-client".');
33
- }
34
-
35
- /**
36
- * A portable implementation of the HttpClientInterface contracts based on Amp's HTTP client.
37
- *
38
- * @author Nicolas Grekas <p@tchwork.com>
39
- */
40
- final class AmpHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface
41
- {
42
- use HttpClientTrait;
43
- use LoggerAwareTrait;
44
-
45
- private $defaultOptions = self::OPTIONS_DEFAULTS;
46
- private static $emptyDefaults = self::OPTIONS_DEFAULTS;
47
-
48
- /** @var AmpClientState */
49
- private $multi;
50
-
51
- /**
52
- * @param array $defaultOptions Default requests' options
53
- * @param callable $clientConfigurator A callable that builds a {@see DelegateHttpClient} from a {@see PooledHttpClient};
54
- * passing null builds an {@see InterceptedHttpClient} with 2 retries on failures
55
- * @param int $maxHostConnections The maximum number of connections to a single host
56
- * @param int $maxPendingPushes The maximum number of pushed responses to accept in the queue
57
- *
58
- * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
59
- */
60
- public function __construct(array $defaultOptions = [], callable $clientConfigurator = null, int $maxHostConnections = 6, int $maxPendingPushes = 50)
61
- {
62
- $this->defaultOptions['buffer'] = $this->defaultOptions['buffer'] ?? \Closure::fromCallable([__CLASS__, 'shouldBuffer']);
63
-
64
- if ($defaultOptions) {
65
- [, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions);
66
- }
67
-
68
- $this->multi = new AmpClientState($clientConfigurator, $maxHostConnections, $maxPendingPushes, $this->logger);
69
- }
70
-
71
- /**
72
- * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
73
- *
74
- * {@inheritdoc}
75
- */
76
- public function request(string $method, string $url, array $options = []): ResponseInterface
77
- {
78
- [$url, $options] = self::prepareRequest($method, $url, $options, $this->defaultOptions);
79
-
80
- $options['proxy'] = self::getProxy($options['proxy'], $url, $options['no_proxy']);
81
-
82
- if (null !== $options['proxy'] && !class_exists(Http1TunnelConnector::class)) {
83
- throw new \LogicException('You cannot use the "proxy" option as the "amphp/http-tunnel" package is not installed. Try running "composer require amphp/http-tunnel".');
84
- }
85
-
86
- if ($options['bindto']) {
87
- if (0 === strpos($options['bindto'], 'if!')) {
88
- throw new TransportException(__CLASS__.' cannot bind to network interfaces, use e.g. CurlHttpClient instead.');
89
- }
90
- if (0 === strpos($options['bindto'], 'host!')) {
91
- $options['bindto'] = substr($options['bindto'], 5);
92
- }
93
- }
94
-
95
- if (('' !== $options['body'] || 'POST' === $method || isset($options['normalized_headers']['content-length'])) && !isset($options['normalized_headers']['content-type'])) {
96
- $options['headers'][] = 'Content-Type: application/x-www-form-urlencoded';
97
- }
98
-
99
- if (!isset($options['normalized_headers']['user-agent'])) {
100
- $options['headers'][] = 'User-Agent: Symfony HttpClient/Amp';
101
- }
102
-
103
- if (0 < $options['max_duration']) {
104
- $options['timeout'] = min($options['max_duration'], $options['timeout']);
105
- }
106
-
107
- if ($options['resolve']) {
108
- $this->multi->dnsCache = $options['resolve'] + $this->multi->dnsCache;
109
- }
110
-
111
- if ($options['peer_fingerprint'] && !isset($options['peer_fingerprint']['pin-sha256'])) {
112
- throw new TransportException(__CLASS__.' supports only "pin-sha256" fingerprints.');
113
- }
114
-
115
- $request = new Request(implode('', $url), $method);
116
-
117
- if ($options['http_version']) {
118
- switch ((float) $options['http_version']) {
119
- case 1.0: $request->setProtocolVersions(['1.0']); break;
120
- case 1.1: $request->setProtocolVersions(['1.1', '1.0']); break;
121
- default: $request->setProtocolVersions(['2', '1.1', '1.0']); break;
122
- }
123
- }
124
-
125
- foreach ($options['headers'] as $v) {
126
- $h = explode(': ', $v, 2);
127
- $request->addHeader($h[0], $h[1]);
128
- }
129
-
130
- $request->setTcpConnectTimeout(1000 * $options['timeout']);
131
- $request->setTlsHandshakeTimeout(1000 * $options['timeout']);
132
- $request->setTransferTimeout(1000 * $options['max_duration']);
133
- if (method_exists($request, 'setInactivityTimeout')) {
134
- $request->setInactivityTimeout(0);
135
- }
136
-
137
- if ('' !== $request->getUri()->getUserInfo() && !$request->hasHeader('authorization')) {
138
- $auth = explode(':', $request->getUri()->getUserInfo(), 2);
139
- $auth = array_map('rawurldecode', $auth) + [1 => ''];
140
- $request->setHeader('Authorization', 'Basic '.base64_encode(implode(':', $auth)));
141
- }
142
-
143
- return new AmpResponse($this->multi, $request, $options, $this->logger);
144
- }
145
-
146
- /**
147
- * {@inheritdoc}
148
- */
149
- public function stream($responses, float $timeout = null): ResponseStreamInterface
150
- {
151
- if ($responses instanceof AmpResponse) {
152
- $responses = [$responses];
153
- } elseif (!is_iterable($responses)) {
154
- throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of AmpResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
155
- }
156
-
157
- return new ResponseStream(AmpResponse::stream($responses, $timeout));
158
- }
159
-
160
- public function reset()
161
- {
162
- $this->multi->dnsCache = [];
163
-
164
- foreach ($this->multi->pushedResponses as $authority => $pushedResponses) {
165
- foreach ($pushedResponses as [$pushedUrl, $pushDeferred]) {
166
- $pushDeferred->fail(new CancelledException());
167
-
168
- if ($this->logger) {
169
- $this->logger->debug(sprintf('Unused pushed response: "%s"', $pushedUrl));
170
- }
171
- }
172
- }
173
-
174
- $this->multi->pushedResponses = [];
175
- }
176
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/AsyncDecoratorTrait.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Symfony\Component\HttpClient\Response\AsyncResponse;
15
- use Symfony\Component\HttpClient\Response\ResponseStream;
16
- use Symfony\Contracts\HttpClient\ResponseInterface;
17
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
18
-
19
- /**
20
- * Eases with processing responses while streaming them.
21
- *
22
- * @author Nicolas Grekas <p@tchwork.com>
23
- */
24
- trait AsyncDecoratorTrait
25
- {
26
- use DecoratorTrait;
27
-
28
- /**
29
- * {@inheritdoc}
30
- *
31
- * @return AsyncResponse
32
- */
33
- abstract public function request(string $method, string $url, array $options = []): ResponseInterface;
34
-
35
- /**
36
- * {@inheritdoc}
37
- */
38
- public function stream($responses, float $timeout = null): ResponseStreamInterface
39
- {
40
- if ($responses instanceof AsyncResponse) {
41
- $responses = [$responses];
42
- } elseif (!is_iterable($responses)) {
43
- throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of AsyncResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
44
- }
45
-
46
- return new ResponseStream(AsyncResponse::stream($responses, $timeout, static::class));
47
- }
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/CHANGELOG.md DELETED
@@ -1,54 +0,0 @@
1
- CHANGELOG
2
- =========
3
-
4
- 5.4
5
- ---
6
-
7
- * Add `MockHttpClient::setResponseFactory()` method to be able to set response factory after client creating
8
-
9
- 5.3
10
- ---
11
-
12
- * Implement `HttpClientInterface::withOptions()` from `symfony/contracts` v2.4
13
- * Add `DecoratorTrait` to ease writing simple decorators
14
-
15
- 5.2.0
16
- -----
17
-
18
- * added `AsyncDecoratorTrait` to ease processing responses without breaking async
19
- * added support for pausing responses with a new `pause_handler` callable exposed as an info item
20
- * added `StreamableInterface` to ease turning responses into PHP streams
21
- * added `MockResponse::getRequestMethod()` and `getRequestUrl()` to allow inspecting which request has been sent
22
- * added `EventSourceHttpClient` a Server-Sent events stream implementing the [EventSource specification](https://www.w3.org/TR/eventsource/#eventsource)
23
- * added option "extra.curl" to allow setting additional curl options in `CurlHttpClient`
24
- * added `RetryableHttpClient` to automatically retry failed HTTP requests.
25
- * added `extra.trace_content` option to `TraceableHttpClient` to prevent it from keeping the content in memory
26
-
27
- 5.1.0
28
- -----
29
-
30
- * added `NoPrivateNetworkHttpClient` decorator
31
- * added `AmpHttpClient`, a portable HTTP/2 implementation based on Amp
32
- * added `LoggerAwareInterface` to `ScopingHttpClient` and `TraceableHttpClient`
33
- * made `HttpClient::create()` return an `AmpHttpClient` when `amphp/http-client` is found but curl is not or too old
34
-
35
- 4.4.0
36
- -----
37
-
38
- * added `canceled` to `ResponseInterface::getInfo()`
39
- * added `HttpClient::createForBaseUri()`
40
- * added `HttplugClient` with support for sync and async requests
41
- * added `max_duration` option
42
- * added support for NTLM authentication
43
- * added `StreamWrapper` to cast any `ResponseInterface` instances to PHP streams.
44
- * added `$response->toStream()` to cast responses to regular PHP streams
45
- * made `Psr18Client` implement relevant PSR-17 factories and have streaming responses
46
- * added `TraceableHttpClient`, `HttpClientDataCollector` and `HttpClientPass` to integrate with the web profiler
47
- * allow enabling buffering conditionally with a Closure
48
- * allow option "buffer" to be a stream resource
49
- * allow arbitrary values for the "json" option
50
-
51
- 4.3.0
52
- -----
53
-
54
- * added the component
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/CachingHttpClient.php DELETED
@@ -1,152 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Symfony\Component\HttpClient\Response\MockResponse;
15
- use Symfony\Component\HttpClient\Response\ResponseStream;
16
- use Symfony\Component\HttpFoundation\Request;
17
- use Symfony\Component\HttpKernel\HttpCache\HttpCache;
18
- use Symfony\Component\HttpKernel\HttpCache\StoreInterface;
19
- use Symfony\Component\HttpKernel\HttpClientKernel;
20
- use Symfony\Contracts\HttpClient\HttpClientInterface;
21
- use Symfony\Contracts\HttpClient\ResponseInterface;
22
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
23
- use Symfony\Contracts\Service\ResetInterface;
24
-
25
- /**
26
- * Adds caching on top of an HTTP client.
27
- *
28
- * The implementation buffers responses in memory and doesn't stream directly from the network.
29
- * You can disable/enable this layer by setting option "no_cache" under "extra" to true/false.
30
- * By default, caching is enabled unless the "buffer" option is set to false.
31
- *
32
- * @author Nicolas Grekas <p@tchwork.com>
33
- */
34
- class CachingHttpClient implements HttpClientInterface, ResetInterface
35
- {
36
- use HttpClientTrait;
37
-
38
- private $client;
39
- private $cache;
40
- private $defaultOptions = self::OPTIONS_DEFAULTS;
41
-
42
- public function __construct(HttpClientInterface $client, StoreInterface $store, array $defaultOptions = [])
43
- {
44
- if (!class_exists(HttpClientKernel::class)) {
45
- throw new \LogicException(sprintf('Using "%s" requires that the HttpKernel component version 4.3 or higher is installed, try running "composer require symfony/http-kernel:^5.4".', __CLASS__));
46
- }
47
-
48
- $this->client = $client;
49
- $kernel = new HttpClientKernel($client);
50
- $this->cache = new HttpCache($kernel, $store, null, $defaultOptions);
51
-
52
- unset($defaultOptions['debug']);
53
- unset($defaultOptions['default_ttl']);
54
- unset($defaultOptions['private_headers']);
55
- unset($defaultOptions['allow_reload']);
56
- unset($defaultOptions['allow_revalidate']);
57
- unset($defaultOptions['stale_while_revalidate']);
58
- unset($defaultOptions['stale_if_error']);
59
- unset($defaultOptions['trace_level']);
60
- unset($defaultOptions['trace_header']);
61
-
62
- if ($defaultOptions) {
63
- [, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions);
64
- }
65
- }
66
-
67
- /**
68
- * {@inheritdoc}
69
- */
70
- public function request(string $method, string $url, array $options = []): ResponseInterface
71
- {
72
- [$url, $options] = $this->prepareRequest($method, $url, $options, $this->defaultOptions, true);
73
- $url = implode('', $url);
74
-
75
- if (!empty($options['body']) || !empty($options['extra']['no_cache']) || !\in_array($method, ['GET', 'HEAD', 'OPTIONS'])) {
76
- return $this->client->request($method, $url, $options);
77
- }
78
-
79
- $request = Request::create($url, $method);
80
- $request->attributes->set('http_client_options', $options);
81
-
82
- foreach ($options['normalized_headers'] as $name => $values) {
83
- if ('cookie' !== $name) {
84
- foreach ($values as $value) {
85
- $request->headers->set($name, substr($value, 2 + \strlen($name)), false);
86
- }
87
-
88
- continue;
89
- }
90
-
91
- foreach ($values as $cookies) {
92
- foreach (explode('; ', substr($cookies, \strlen('Cookie: '))) as $cookie) {
93
- if ('' !== $cookie) {
94
- $cookie = explode('=', $cookie, 2);
95
- $request->cookies->set($cookie[0], $cookie[1] ?? '');
96
- }
97
- }
98
- }
99
- }
100
-
101
- $response = $this->cache->handle($request);
102
- $response = new MockResponse($response->getContent(), [
103
- 'http_code' => $response->getStatusCode(),
104
- 'response_headers' => $response->headers->allPreserveCase(),
105
- ]);
106
-
107
- return MockResponse::fromRequest($method, $url, $options, $response);
108
- }
109
-
110
- /**
111
- * {@inheritdoc}
112
- */
113
- public function stream($responses, float $timeout = null): ResponseStreamInterface
114
- {
115
- if ($responses instanceof ResponseInterface) {
116
- $responses = [$responses];
117
- } elseif (!is_iterable($responses)) {
118
- throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of ResponseInterface objects, "%s" given.', __METHOD__, get_debug_type($responses)));
119
- }
120
-
121
- $mockResponses = [];
122
- $clientResponses = [];
123
-
124
- foreach ($responses as $response) {
125
- if ($response instanceof MockResponse) {
126
- $mockResponses[] = $response;
127
- } else {
128
- $clientResponses[] = $response;
129
- }
130
- }
131
-
132
- if (!$mockResponses) {
133
- return $this->client->stream($clientResponses, $timeout);
134
- }
135
-
136
- if (!$clientResponses) {
137
- return new ResponseStream(MockResponse::stream($mockResponses, $timeout));
138
- }
139
-
140
- return new ResponseStream((function () use ($mockResponses, $clientResponses, $timeout) {
141
- yield from MockResponse::stream($mockResponses, $timeout);
142
- yield $this->client->stream($clientResponses, $timeout);
143
- })());
144
- }
145
-
146
- public function reset()
147
- {
148
- if ($this->client instanceof ResetInterface) {
149
- $this->client->reset();
150
- }
151
- }
152
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Chunk/DataChunk.php DELETED
@@ -1,87 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Chunk;
13
-
14
- use Symfony\Contracts\HttpClient\ChunkInterface;
15
-
16
- /**
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- *
19
- * @internal
20
- */
21
- class DataChunk implements ChunkInterface
22
- {
23
- private $offset = 0;
24
- private $content = '';
25
-
26
- public function __construct(int $offset = 0, string $content = '')
27
- {
28
- $this->offset = $offset;
29
- $this->content = $content;
30
- }
31
-
32
- /**
33
- * {@inheritdoc}
34
- */
35
- public function isTimeout(): bool
36
- {
37
- return false;
38
- }
39
-
40
- /**
41
- * {@inheritdoc}
42
- */
43
- public function isFirst(): bool
44
- {
45
- return false;
46
- }
47
-
48
- /**
49
- * {@inheritdoc}
50
- */
51
- public function isLast(): bool
52
- {
53
- return false;
54
- }
55
-
56
- /**
57
- * {@inheritdoc}
58
- */
59
- public function getInformationalStatus(): ?array
60
- {
61
- return null;
62
- }
63
-
64
- /**
65
- * {@inheritdoc}
66
- */
67
- public function getContent(): string
68
- {
69
- return $this->content;
70
- }
71
-
72
- /**
73
- * {@inheritdoc}
74
- */
75
- public function getOffset(): int
76
- {
77
- return $this->offset;
78
- }
79
-
80
- /**
81
- * {@inheritdoc}
82
- */
83
- public function getError(): ?string
84
- {
85
- return null;
86
- }
87
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Chunk/ErrorChunk.php DELETED
@@ -1,140 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Chunk;
13
-
14
- use Symfony\Component\HttpClient\Exception\TimeoutException;
15
- use Symfony\Component\HttpClient\Exception\TransportException;
16
- use Symfony\Contracts\HttpClient\ChunkInterface;
17
-
18
- /**
19
- * @author Nicolas Grekas <p@tchwork.com>
20
- *
21
- * @internal
22
- */
23
- class ErrorChunk implements ChunkInterface
24
- {
25
- private $didThrow = false;
26
- private $offset;
27
- private $errorMessage;
28
- private $error;
29
-
30
- /**
31
- * @param \Throwable|string $error
32
- */
33
- public function __construct(int $offset, $error)
34
- {
35
- $this->offset = $offset;
36
-
37
- if (\is_string($error)) {
38
- $this->errorMessage = $error;
39
- } else {
40
- $this->error = $error;
41
- $this->errorMessage = $error->getMessage();
42
- }
43
- }
44
-
45
- /**
46
- * {@inheritdoc}
47
- */
48
- public function isTimeout(): bool
49
- {
50
- $this->didThrow = true;
51
-
52
- if (null !== $this->error) {
53
- throw new TransportException($this->errorMessage, 0, $this->error);
54
- }
55
-
56
- return true;
57
- }
58
-
59
- /**
60
- * {@inheritdoc}
61
- */
62
- public function isFirst(): bool
63
- {
64
- $this->didThrow = true;
65
- throw null !== $this->error ? new TransportException($this->errorMessage, 0, $this->error) : new TimeoutException($this->errorMessage);
66
- }
67
-
68
- /**
69
- * {@inheritdoc}
70
- */
71
- public function isLast(): bool
72
- {
73
- $this->didThrow = true;
74
- throw null !== $this->error ? new TransportException($this->errorMessage, 0, $this->error) : new TimeoutException($this->errorMessage);
75
- }
76
-
77
- /**
78
- * {@inheritdoc}
79
- */
80
- public function getInformationalStatus(): ?array
81
- {
82
- $this->didThrow = true;
83
- throw null !== $this->error ? new TransportException($this->errorMessage, 0, $this->error) : new TimeoutException($this->errorMessage);
84
- }
85
-
86
- /**
87
- * {@inheritdoc}
88
- */
89
- public function getContent(): string
90
- {
91
- $this->didThrow = true;
92
- throw null !== $this->error ? new TransportException($this->errorMessage, 0, $this->error) : new TimeoutException($this->errorMessage);
93
- }
94
-
95
- /**
96
- * {@inheritdoc}
97
- */
98
- public function getOffset(): int
99
- {
100
- return $this->offset;
101
- }
102
-
103
- /**
104
- * {@inheritdoc}
105
- */
106
- public function getError(): ?string
107
- {
108
- return $this->errorMessage;
109
- }
110
-
111
- /**
112
- * @return bool Whether the wrapped error has been thrown or not
113
- */
114
- public function didThrow(bool $didThrow = null): bool
115
- {
116
- if (null !== $didThrow && $this->didThrow !== $didThrow) {
117
- return !$this->didThrow = $didThrow;
118
- }
119
-
120
- return $this->didThrow;
121
- }
122
-
123
- public function __sleep(): array
124
- {
125
- throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
126
- }
127
-
128
- public function __wakeup()
129
- {
130
- throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
131
- }
132
-
133
- public function __destruct()
134
- {
135
- if (!$this->didThrow) {
136
- $this->didThrow = true;
137
- throw null !== $this->error ? new TransportException($this->errorMessage, 0, $this->error) : new TimeoutException($this->errorMessage);
138
- }
139
- }
140
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Chunk/FirstChunk.php DELETED
@@ -1,28 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Chunk;
13
-
14
- /**
15
- * @author Nicolas Grekas <p@tchwork.com>
16
- *
17
- * @internal
18
- */
19
- class FirstChunk extends DataChunk
20
- {
21
- /**
22
- * {@inheritdoc}
23
- */
24
- public function isFirst(): bool
25
- {
26
- return true;
27
- }
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Chunk/InformationalChunk.php DELETED
@@ -1,35 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Chunk;
13
-
14
- /**
15
- * @author Nicolas Grekas <p@tchwork.com>
16
- *
17
- * @internal
18
- */
19
- class InformationalChunk extends DataChunk
20
- {
21
- private $status;
22
-
23
- public function __construct(int $statusCode, array $headers)
24
- {
25
- $this->status = [$statusCode, $headers];
26
- }
27
-
28
- /**
29
- * {@inheritdoc}
30
- */
31
- public function getInformationalStatus(): ?array
32
- {
33
- return $this->status;
34
- }
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Chunk/LastChunk.php DELETED
@@ -1,28 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Chunk;
13
-
14
- /**
15
- * @author Nicolas Grekas <p@tchwork.com>
16
- *
17
- * @internal
18
- */
19
- class LastChunk extends DataChunk
20
- {
21
- /**
22
- * {@inheritdoc}
23
- */
24
- public function isLast(): bool
25
- {
26
- return true;
27
- }
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Chunk/ServerSentEvent.php DELETED
@@ -1,79 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Chunk;
13
-
14
- use Symfony\Contracts\HttpClient\ChunkInterface;
15
-
16
- /**
17
- * @author Antoine Bluchet <soyuka@gmail.com>
18
- * @author Nicolas Grekas <p@tchwork.com>
19
- */
20
- final class ServerSentEvent extends DataChunk implements ChunkInterface
21
- {
22
- private $data = '';
23
- private $id = '';
24
- private $type = 'message';
25
- private $retry = 0;
26
-
27
- public function __construct(string $content)
28
- {
29
- parent::__construct(-1, $content);
30
-
31
- // remove BOM
32
- if (0 === strpos($content, "\xEF\xBB\xBF")) {
33
- $content = substr($content, 3);
34
- }
35
-
36
- foreach (preg_split("/(?:\r\n|[\r\n])/", $content) as $line) {
37
- if (0 === $i = strpos($line, ':')) {
38
- continue;
39
- }
40
-
41
- $i = false === $i ? \strlen($line) : $i;
42
- $field = substr($line, 0, $i);
43
- $i += 1 + (' ' === ($line[1 + $i] ?? ''));
44
-
45
- switch ($field) {
46
- case 'id': $this->id = substr($line, $i); break;
47
- case 'event': $this->type = substr($line, $i); break;
48
- case 'data': $this->data .= ('' === $this->data ? '' : "\n").substr($line, $i); break;
49
- case 'retry':
50
- $retry = substr($line, $i);
51
-
52
- if ('' !== $retry && \strlen($retry) === strspn($retry, '0123456789')) {
53
- $this->retry = $retry / 1000.0;
54
- }
55
- break;
56
- }
57
- }
58
- }
59
-
60
- public function getId(): string
61
- {
62
- return $this->id;
63
- }
64
-
65
- public function getType(): string
66
- {
67
- return $this->type;
68
- }
69
-
70
- public function getData(): string
71
- {
72
- return $this->data;
73
- }
74
-
75
- public function getRetry(): float
76
- {
77
- return $this->retry;
78
- }
79
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/CurlHttpClient.php DELETED
@@ -1,551 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Psr\Log\LoggerAwareInterface;
15
- use Psr\Log\LoggerInterface;
16
- use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
17
- use Symfony\Component\HttpClient\Exception\TransportException;
18
- use Symfony\Component\HttpClient\Internal\CurlClientState;
19
- use Symfony\Component\HttpClient\Internal\PushedResponse;
20
- use Symfony\Component\HttpClient\Response\CurlResponse;
21
- use Symfony\Component\HttpClient\Response\ResponseStream;
22
- use Symfony\Contracts\HttpClient\HttpClientInterface;
23
- use Symfony\Contracts\HttpClient\ResponseInterface;
24
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
25
- use Symfony\Contracts\Service\ResetInterface;
26
-
27
- /**
28
- * A performant implementation of the HttpClientInterface contracts based on the curl extension.
29
- *
30
- * This provides fully concurrent HTTP requests, with transparent
31
- * HTTP/2 push when a curl version that supports it is installed.
32
- *
33
- * @author Nicolas Grekas <p@tchwork.com>
34
- */
35
- final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface
36
- {
37
- use HttpClientTrait;
38
-
39
- private $defaultOptions = self::OPTIONS_DEFAULTS + [
40
- 'auth_ntlm' => null, // array|string - an array containing the username as first value, and optionally the
41
- // password as the second one; or string like username:password - enabling NTLM auth
42
- 'extra' => [
43
- 'curl' => [], // A list of extra curl options indexed by their corresponding CURLOPT_*
44
- ],
45
- ];
46
- private static $emptyDefaults = self::OPTIONS_DEFAULTS + ['auth_ntlm' => null];
47
-
48
- /**
49
- * @var LoggerInterface|null
50
- */
51
- private $logger;
52
-
53
- /**
54
- * An internal object to share state between the client and its responses.
55
- *
56
- * @var CurlClientState
57
- */
58
- private $multi;
59
-
60
- /**
61
- * @param array $defaultOptions Default request's options
62
- * @param int $maxHostConnections The maximum number of connections to a single host
63
- * @param int $maxPendingPushes The maximum number of pushed responses to accept in the queue
64
- *
65
- * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
66
- */
67
- public function __construct(array $defaultOptions = [], int $maxHostConnections = 6, int $maxPendingPushes = 50)
68
- {
69
- if (!\extension_loaded('curl')) {
70
- throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\CurlHttpClient" as the "curl" extension is not installed.');
71
- }
72
-
73
- $this->defaultOptions['buffer'] = $this->defaultOptions['buffer'] ?? \Closure::fromCallable([__CLASS__, 'shouldBuffer']);
74
-
75
- if ($defaultOptions) {
76
- [, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions);
77
- }
78
-
79
- $this->multi = new CurlClientState($maxHostConnections, $maxPendingPushes);
80
- }
81
-
82
- public function setLogger(LoggerInterface $logger): void
83
- {
84
- $this->logger = $this->multi->logger = $logger;
85
- }
86
-
87
- /**
88
- * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
89
- *
90
- * {@inheritdoc}
91
- */
92
- public function request(string $method, string $url, array $options = []): ResponseInterface
93
- {
94
- [$url, $options] = self::prepareRequest($method, $url, $options, $this->defaultOptions);
95
- $scheme = $url['scheme'];
96
- $authority = $url['authority'];
97
- $host = parse_url($authority, \PHP_URL_HOST);
98
- $proxy = $options['proxy']
99
- ?? ('https:' === $url['scheme'] ? $_SERVER['https_proxy'] ?? $_SERVER['HTTPS_PROXY'] ?? null : null)
100
- // Ignore HTTP_PROXY except on the CLI to work around httpoxy set of vulnerabilities
101
- ?? $_SERVER['http_proxy'] ?? (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? $_SERVER['HTTP_PROXY'] ?? null : null) ?? $_SERVER['all_proxy'] ?? $_SERVER['ALL_PROXY'] ?? null;
102
- $url = implode('', $url);
103
-
104
- if (!isset($options['normalized_headers']['user-agent'])) {
105
- $options['headers'][] = 'User-Agent: Symfony HttpClient/Curl';
106
- }
107
-
108
- $curlopts = [
109
- \CURLOPT_URL => $url,
110
- \CURLOPT_TCP_NODELAY => true,
111
- \CURLOPT_PROTOCOLS => \CURLPROTO_HTTP | \CURLPROTO_HTTPS,
112
- \CURLOPT_REDIR_PROTOCOLS => \CURLPROTO_HTTP | \CURLPROTO_HTTPS,
113
- \CURLOPT_FOLLOWLOCATION => true,
114
- \CURLOPT_MAXREDIRS => 0 < $options['max_redirects'] ? $options['max_redirects'] : 0,
115
- \CURLOPT_COOKIEFILE => '', // Keep track of cookies during redirects
116
- \CURLOPT_TIMEOUT => 0,
117
- \CURLOPT_PROXY => $proxy,
118
- \CURLOPT_NOPROXY => $options['no_proxy'] ?? $_SERVER['no_proxy'] ?? $_SERVER['NO_PROXY'] ?? '',
119
- \CURLOPT_SSL_VERIFYPEER => $options['verify_peer'],
120
- \CURLOPT_SSL_VERIFYHOST => $options['verify_host'] ? 2 : 0,
121
- \CURLOPT_CAINFO => $options['cafile'],
122
- \CURLOPT_CAPATH => $options['capath'],
123
- \CURLOPT_SSL_CIPHER_LIST => $options['ciphers'],
124
- \CURLOPT_SSLCERT => $options['local_cert'],
125
- \CURLOPT_SSLKEY => $options['local_pk'],
126
- \CURLOPT_KEYPASSWD => $options['passphrase'],
127
- \CURLOPT_CERTINFO => $options['capture_peer_cert_chain'],
128
- ];
129
-
130
- if (1.0 === (float) $options['http_version']) {
131
- $curlopts[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0;
132
- } elseif (1.1 === (float) $options['http_version']) {
133
- $curlopts[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1;
134
- } elseif (\defined('CURL_VERSION_HTTP2') && (\CURL_VERSION_HTTP2 & CurlClientState::$curlVersion['features']) && ('https:' === $scheme || 2.0 === (float) $options['http_version'])) {
135
- $curlopts[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0;
136
- }
137
-
138
- if (isset($options['auth_ntlm'])) {
139
- $curlopts[\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM;
140
- $curlopts[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1;
141
-
142
- if (\is_array($options['auth_ntlm'])) {
143
- $count = \count($options['auth_ntlm']);
144
- if ($count <= 0 || $count > 2) {
145
- throw new InvalidArgumentException(sprintf('Option "auth_ntlm" must contain 1 or 2 elements, %d given.', $count));
146
- }
147
-
148
- $options['auth_ntlm'] = implode(':', $options['auth_ntlm']);
149
- }
150
-
151
- if (!\is_string($options['auth_ntlm'])) {
152
- throw new InvalidArgumentException(sprintf('Option "auth_ntlm" must be a string or an array, "%s" given.', get_debug_type($options['auth_ntlm'])));
153
- }
154
-
155
- $curlopts[\CURLOPT_USERPWD] = $options['auth_ntlm'];
156
- }
157
-
158
- if (!\ZEND_THREAD_SAFE) {
159
- $curlopts[\CURLOPT_DNS_USE_GLOBAL_CACHE] = false;
160
- }
161
-
162
- if (\defined('CURLOPT_HEADEROPT') && \defined('CURLHEADER_SEPARATE')) {
163
- $curlopts[\CURLOPT_HEADEROPT] = \CURLHEADER_SEPARATE;
164
- }
165
-
166
- // curl's resolve feature varies by host:port but ours varies by host only, let's handle this with our own DNS map
167
- if (isset($this->multi->dnsCache->hostnames[$host])) {
168
- $options['resolve'] += [$host => $this->multi->dnsCache->hostnames[$host]];
169
- }
170
-
171
- if ($options['resolve'] || $this->multi->dnsCache->evictions) {
172
- // First reset any old DNS cache entries then add the new ones
173
- $resolve = $this->multi->dnsCache->evictions;
174
- $this->multi->dnsCache->evictions = [];
175
- $port = parse_url($authority, \PHP_URL_PORT) ?: ('http:' === $scheme ? 80 : 443);
176
-
177
- if ($resolve && 0x072A00 > CurlClientState::$curlVersion['version_number']) {
178
- // DNS cache removals require curl 7.42 or higher
179
- $this->multi->reset();
180
- }
181
-
182
- foreach ($options['resolve'] as $host => $ip) {
183
- $resolve[] = null === $ip ? "-$host:$port" : "$host:$port:$ip";
184
- $this->multi->dnsCache->hostnames[$host] = $ip;
185
- $this->multi->dnsCache->removals["-$host:$port"] = "-$host:$port";
186
- }
187
-
188
- $curlopts[\CURLOPT_RESOLVE] = $resolve;
189
- }
190
-
191
- if ('POST' === $method) {
192
- // Use CURLOPT_POST to have browser-like POST-to-GET redirects for 301, 302 and 303
193
- $curlopts[\CURLOPT_POST] = true;
194
- } elseif ('HEAD' === $method) {
195
- $curlopts[\CURLOPT_NOBODY] = true;
196
- } else {
197
- $curlopts[\CURLOPT_CUSTOMREQUEST] = $method;
198
- }
199
-
200
- if ('\\' !== \DIRECTORY_SEPARATOR && $options['timeout'] < 1) {
201
- $curlopts[\CURLOPT_NOSIGNAL] = true;
202
- }
203
-
204
- if (\extension_loaded('zlib') && !isset($options['normalized_headers']['accept-encoding'])) {
205
- $options['headers'][] = 'Accept-Encoding: gzip'; // Expose only one encoding, some servers mess up when more are provided
206
- }
207
-
208
- foreach ($options['headers'] as $header) {
209
- if (':' === $header[-2] && \strlen($header) - 2 === strpos($header, ': ')) {
210
- // curl requires a special syntax to send empty headers
211
- $curlopts[\CURLOPT_HTTPHEADER][] = substr_replace($header, ';', -2);
212
- } else {
213
- $curlopts[\CURLOPT_HTTPHEADER][] = $header;
214
- }
215
- }
216
-
217
- // Prevent curl from sending its default Accept and Expect headers
218
- foreach (['accept', 'expect'] as $header) {
219
- if (!isset($options['normalized_headers'][$header][0])) {
220
- $curlopts[\CURLOPT_HTTPHEADER][] = $header.':';
221
- }
222
- }
223
-
224
- if (!\is_string($body = $options['body'])) {
225
- if (\is_resource($body)) {
226
- $curlopts[\CURLOPT_INFILE] = $body;
227
- } else {
228
- $eof = false;
229
- $buffer = '';
230
- $curlopts[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use ($body, &$buffer, &$eof) {
231
- return self::readRequestBody($length, $body, $buffer, $eof);
232
- };
233
- }
234
-
235
- if (isset($options['normalized_headers']['content-length'][0])) {
236
- $curlopts[\CURLOPT_INFILESIZE] = substr($options['normalized_headers']['content-length'][0], \strlen('Content-Length: '));
237
- } elseif (!isset($options['normalized_headers']['transfer-encoding'])) {
238
- $curlopts[\CURLOPT_HTTPHEADER][] = 'Transfer-Encoding: chunked'; // Enable chunked request bodies
239
- }
240
-
241
- if ('POST' !== $method) {
242
- $curlopts[\CURLOPT_UPLOAD] = true;
243
-
244
- if (!isset($options['normalized_headers']['content-type'])) {
245
- $curlopts[\CURLOPT_HTTPHEADER][] = 'Content-Type: application/x-www-form-urlencoded';
246
- }
247
- }
248
- } elseif ('' !== $body || 'POST' === $method) {
249
- $curlopts[\CURLOPT_POSTFIELDS] = $body;
250
- }
251
-
252
- if ($options['peer_fingerprint']) {
253
- if (!isset($options['peer_fingerprint']['pin-sha256'])) {
254
- throw new TransportException(__CLASS__.' supports only "pin-sha256" fingerprints.');
255
- }
256
-
257
- $curlopts[\CURLOPT_PINNEDPUBLICKEY] = 'sha256//'.implode(';sha256//', $options['peer_fingerprint']['pin-sha256']);
258
- }
259
-
260
- if ($options['bindto']) {
261
- if (file_exists($options['bindto'])) {
262
- $curlopts[\CURLOPT_UNIX_SOCKET_PATH] = $options['bindto'];
263
- } elseif (!str_starts_with($options['bindto'], 'if!') && preg_match('/^(.*):(\d+)$/', $options['bindto'], $matches)) {
264
- $curlopts[\CURLOPT_INTERFACE] = $matches[1];
265
- $curlopts[\CURLOPT_LOCALPORT] = $matches[2];
266
- } else {
267
- $curlopts[\CURLOPT_INTERFACE] = $options['bindto'];
268
- }
269
- }
270
-
271
- if (0 < $options['max_duration']) {
272
- $curlopts[\CURLOPT_TIMEOUT_MS] = 1000 * $options['max_duration'];
273
- }
274
-
275
- if (!empty($options['extra']['curl']) && \is_array($options['extra']['curl'])) {
276
- $this->validateExtraCurlOptions($options['extra']['curl']);
277
- $curlopts += $options['extra']['curl'];
278
- }
279
-
280
- if ($pushedResponse = $this->multi->pushedResponses[$url] ?? null) {
281
- unset($this->multi->pushedResponses[$url]);
282
-
283
- if (self::acceptPushForRequest($method, $options, $pushedResponse)) {
284
- $this->logger && $this->logger->debug(sprintf('Accepting pushed response: "%s %s"', $method, $url));
285
-
286
- // Reinitialize the pushed response with request's options
287
- $ch = $pushedResponse->handle;
288
- $pushedResponse = $pushedResponse->response;
289
- $pushedResponse->__construct($this->multi, $url, $options, $this->logger);
290
- } else {
291
- $this->logger && $this->logger->debug(sprintf('Rejecting pushed response: "%s"', $url));
292
- $pushedResponse = null;
293
- }
294
- }
295
-
296
- if (!$pushedResponse) {
297
- $ch = curl_init();
298
- $this->logger && $this->logger->info(sprintf('Request: "%s %s"', $method, $url));
299
- $curlopts += [\CURLOPT_SHARE => $this->multi->share];
300
- }
301
-
302
- foreach ($curlopts as $opt => $value) {
303
- if (null !== $value && !curl_setopt($ch, $opt, $value) && \CURLOPT_CERTINFO !== $opt && (!\defined('CURLOPT_HEADEROPT') || \CURLOPT_HEADEROPT !== $opt)) {
304
- $constantName = $this->findConstantName($opt);
305
- throw new TransportException(sprintf('Curl option "%s" is not supported.', $constantName ?? $opt));
306
- }
307
- }
308
-
309
- return $pushedResponse ?? new CurlResponse($this->multi, $ch, $options, $this->logger, $method, self::createRedirectResolver($options, $host), CurlClientState::$curlVersion['version_number']);
310
- }
311
-
312
- /**
313
- * {@inheritdoc}
314
- */
315
- public function stream($responses, float $timeout = null): ResponseStreamInterface
316
- {
317
- if ($responses instanceof CurlResponse) {
318
- $responses = [$responses];
319
- } elseif (!is_iterable($responses)) {
320
- throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of CurlResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
321
- }
322
-
323
- if (\is_resource($this->multi->handle) || $this->multi->handle instanceof \CurlMultiHandle) {
324
- $active = 0;
325
- while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($this->multi->handle, $active)) {
326
- }
327
- }
328
-
329
- return new ResponseStream(CurlResponse::stream($responses, $timeout));
330
- }
331
-
332
- public function reset()
333
- {
334
- $this->multi->reset();
335
- }
336
-
337
- /**
338
- * Accepts pushed responses only if their headers related to authentication match the request.
339
- */
340
- private static function acceptPushForRequest(string $method, array $options, PushedResponse $pushedResponse): bool
341
- {
342
- if ('' !== $options['body'] || $method !== $pushedResponse->requestHeaders[':method'][0]) {
343
- return false;
344
- }
345
-
346
- foreach (['proxy', 'no_proxy', 'bindto', 'local_cert', 'local_pk'] as $k) {
347
- if ($options[$k] !== $pushedResponse->parentOptions[$k]) {
348
- return false;
349
- }
350
- }
351
-
352
- foreach (['authorization', 'cookie', 'range', 'proxy-authorization'] as $k) {
353
- $normalizedHeaders = $options['normalized_headers'][$k] ?? [];
354
- foreach ($normalizedHeaders as $i => $v) {
355
- $normalizedHeaders[$i] = substr($v, \strlen($k) + 2);
356
- }
357
-
358
- if (($pushedResponse->requestHeaders[$k] ?? []) !== $normalizedHeaders) {
359
- return false;
360
- }
361
- }
362
-
363
- return true;
364
- }
365
-
366
- /**
367
- * Wraps the request's body callback to allow it to return strings longer than curl requested.
368
- */
369
- private static function readRequestBody(int $length, \Closure $body, string &$buffer, bool &$eof): string
370
- {
371
- if (!$eof && \strlen($buffer) < $length) {
372
- if (!\is_string($data = $body($length))) {
373
- throw new TransportException(sprintf('The return value of the "body" option callback must be a string, "%s" returned.', get_debug_type($data)));
374
- }
375
-
376
- $buffer .= $data;
377
- $eof = '' === $data;
378
- }
379
-
380
- $data = substr($buffer, 0, $length);
381
- $buffer = substr($buffer, $length);
382
-
383
- return $data;
384
- }
385
-
386
- /**
387
- * Resolves relative URLs on redirects and deals with authentication headers.
388
- *
389
- * Work around CVE-2018-1000007: Authorization and Cookie headers should not follow redirects - fixed in Curl 7.64
390
- */
391
- private static function createRedirectResolver(array $options, string $host): \Closure
392
- {
393
- $redirectHeaders = [];
394
- if (0 < $options['max_redirects']) {
395
- $redirectHeaders['host'] = $host;
396
- $redirectHeaders['with_auth'] = $redirectHeaders['no_auth'] = array_filter($options['headers'], static function ($h) {
397
- return 0 !== stripos($h, 'Host:');
398
- });
399
-
400
- if (isset($options['normalized_headers']['authorization'][0]) || isset($options['normalized_headers']['cookie'][0])) {
401
- $redirectHeaders['no_auth'] = array_filter($options['headers'], static function ($h) {
402
- return 0 !== stripos($h, 'Authorization:') && 0 !== stripos($h, 'Cookie:');
403
- });
404
- }
405
- }
406
-
407
- return static function ($ch, string $location, bool $noContent) use (&$redirectHeaders) {
408
- try {
409
- $location = self::parseUrl($location);
410
- } catch (InvalidArgumentException $e) {
411
- return null;
412
- }
413
-
414
- if ($noContent && $redirectHeaders) {
415
- $filterContentHeaders = static function ($h) {
416
- return 0 !== stripos($h, 'Content-Length:') && 0 !== stripos($h, 'Content-Type:') && 0 !== stripos($h, 'Transfer-Encoding:');
417
- };
418
- $redirectHeaders['no_auth'] = array_filter($redirectHeaders['no_auth'], $filterContentHeaders);
419
- $redirectHeaders['with_auth'] = array_filter($redirectHeaders['with_auth'], $filterContentHeaders);
420
- }
421
-
422
- if ($redirectHeaders && $host = parse_url('http:'.$location['authority'], \PHP_URL_HOST)) {
423
- $requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
424
- curl_setopt($ch, \CURLOPT_HTTPHEADER, $requestHeaders);
425
- } elseif ($noContent && $redirectHeaders) {
426
- curl_setopt($ch, \CURLOPT_HTTPHEADER, $redirectHeaders['with_auth']);
427
- }
428
-
429
- $url = self::parseUrl(curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL));
430
- $url = self::resolveUrl($location, $url);
431
-
432
- curl_setopt($ch, \CURLOPT_PROXY, $options['proxy']
433
- ?? ('https:' === $url['scheme'] ? $_SERVER['https_proxy'] ?? $_SERVER['HTTPS_PROXY'] ?? null : null)
434
- // Ignore HTTP_PROXY except on the CLI to work around httpoxy set of vulnerabilities
435
- ?? $_SERVER['http_proxy'] ?? (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? $_SERVER['HTTP_PROXY'] ?? null : null) ?? $_SERVER['all_proxy'] ?? $_SERVER['ALL_PROXY'] ?? null
436
- );
437
-
438
- return implode('', $url);
439
- };
440
- }
441
-
442
- private function findConstantName(int $opt): ?string
443
- {
444
- $constants = array_filter(get_defined_constants(), static function ($v, $k) use ($opt) {
445
- return $v === $opt && 'C' === $k[0] && (str_starts_with($k, 'CURLOPT_') || str_starts_with($k, 'CURLINFO_'));
446
- }, \ARRAY_FILTER_USE_BOTH);
447
-
448
- return key($constants);
449
- }
450
-
451
- /**
452
- * Prevents overriding options that are set internally throughout the request.
453
- */
454
- private function validateExtraCurlOptions(array $options): void
455
- {
456
- $curloptsToConfig = [
457
- //options used in CurlHttpClient
458
- \CURLOPT_HTTPAUTH => 'auth_ntlm',
459
- \CURLOPT_USERPWD => 'auth_ntlm',
460
- \CURLOPT_RESOLVE => 'resolve',
461
- \CURLOPT_NOSIGNAL => 'timeout',
462
- \CURLOPT_HTTPHEADER => 'headers',
463
- \CURLOPT_INFILE => 'body',
464
- \CURLOPT_READFUNCTION => 'body',
465
- \CURLOPT_INFILESIZE => 'body',
466
- \CURLOPT_POSTFIELDS => 'body',
467
- \CURLOPT_UPLOAD => 'body',
468
- \CURLOPT_INTERFACE => 'bindto',
469
- \CURLOPT_TIMEOUT_MS => 'max_duration',
470
- \CURLOPT_TIMEOUT => 'max_duration',
471
- \CURLOPT_MAXREDIRS => 'max_redirects',
472
- \CURLOPT_PROXY => 'proxy',
473
- \CURLOPT_NOPROXY => 'no_proxy',
474
- \CURLOPT_SSL_VERIFYPEER => 'verify_peer',
475
- \CURLOPT_SSL_VERIFYHOST => 'verify_host',
476
- \CURLOPT_CAINFO => 'cafile',
477
- \CURLOPT_CAPATH => 'capath',
478
- \CURLOPT_SSL_CIPHER_LIST => 'ciphers',
479
- \CURLOPT_SSLCERT => 'local_cert',
480
- \CURLOPT_SSLKEY => 'local_pk',
481
- \CURLOPT_KEYPASSWD => 'passphrase',
482
- \CURLOPT_CERTINFO => 'capture_peer_cert_chain',
483
- \CURLOPT_USERAGENT => 'normalized_headers',
484
- \CURLOPT_REFERER => 'headers',
485
- //options used in CurlResponse
486
- \CURLOPT_NOPROGRESS => 'on_progress',
487
- \CURLOPT_PROGRESSFUNCTION => 'on_progress',
488
- ];
489
-
490
- if (\defined('CURLOPT_UNIX_SOCKET_PATH')) {
491
- $curloptsToConfig[\CURLOPT_UNIX_SOCKET_PATH] = 'bindto';
492
- }
493
-
494
- if (\defined('CURLOPT_PINNEDPUBLICKEY')) {
495
- $curloptsToConfig[\CURLOPT_PINNEDPUBLICKEY] = 'peer_fingerprint';
496
- }
497
-
498
- $curloptsToCheck = [
499
- \CURLOPT_PRIVATE,
500
- \CURLOPT_HEADERFUNCTION,
501
- \CURLOPT_WRITEFUNCTION,
502
- \CURLOPT_VERBOSE,
503
- \CURLOPT_STDERR,
504
- \CURLOPT_RETURNTRANSFER,
505
- \CURLOPT_URL,
506
- \CURLOPT_FOLLOWLOCATION,
507
- \CURLOPT_HEADER,
508
- \CURLOPT_CONNECTTIMEOUT,
509
- \CURLOPT_CONNECTTIMEOUT_MS,
510
- \CURLOPT_HTTP_VERSION,
511
- \CURLOPT_PORT,
512
- \CURLOPT_DNS_USE_GLOBAL_CACHE,
513
- \CURLOPT_PROTOCOLS,
514
- \CURLOPT_REDIR_PROTOCOLS,
515
- \CURLOPT_COOKIEFILE,
516
- \CURLINFO_REDIRECT_COUNT,
517
- ];
518
-
519
- if (\defined('CURLOPT_HTTP09_ALLOWED')) {
520
- $curloptsToCheck[] = \CURLOPT_HTTP09_ALLOWED;
521
- }
522
-
523
- if (\defined('CURLOPT_HEADEROPT')) {
524
- $curloptsToCheck[] = \CURLOPT_HEADEROPT;
525
- }
526
-
527
- $methodOpts = [
528
- \CURLOPT_POST,
529
- \CURLOPT_PUT,
530
- \CURLOPT_CUSTOMREQUEST,
531
- \CURLOPT_HTTPGET,
532
- \CURLOPT_NOBODY,
533
- ];
534
-
535
- foreach ($options as $opt => $optValue) {
536
- if (isset($curloptsToConfig[$opt])) {
537
- $constName = $this->findConstantName($opt) ?? $opt;
538
- throw new InvalidArgumentException(sprintf('Cannot set "%s" with "extra.curl", use option "%s" instead.', $constName, $curloptsToConfig[$opt]));
539
- }
540
-
541
- if (\in_array($opt, $methodOpts)) {
542
- throw new InvalidArgumentException('The HTTP method cannot be overridden using "extra.curl".');
543
- }
544
-
545
- if (\in_array($opt, $curloptsToCheck)) {
546
- $constName = $this->findConstantName($opt) ?? $opt;
547
- throw new InvalidArgumentException(sprintf('Cannot set "%s" with "extra.curl".', $constName));
548
- }
549
- }
550
- }
551
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/DataCollector/HttpClientDataCollector.php DELETED
@@ -1,170 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\DataCollector;
13
-
14
- use Symfony\Component\HttpClient\TraceableHttpClient;
15
- use Symfony\Component\HttpFoundation\Request;
16
- use Symfony\Component\HttpFoundation\Response;
17
- use Symfony\Component\HttpKernel\DataCollector\DataCollector;
18
- use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
19
- use Symfony\Component\VarDumper\Caster\ImgStub;
20
-
21
- /**
22
- * @author Jérémy Romey <jeremy@free-agent.fr>
23
- */
24
- final class HttpClientDataCollector extends DataCollector implements LateDataCollectorInterface
25
- {
26
- /**
27
- * @var TraceableHttpClient[]
28
- */
29
- private $clients = [];
30
-
31
- public function registerClient(string $name, TraceableHttpClient $client)
32
- {
33
- $this->clients[$name] = $client;
34
- }
35
-
36
- /**
37
- * {@inheritdoc}
38
- */
39
- public function collect(Request $request, Response $response, \Throwable $exception = null)
40
- {
41
- $this->reset();
42
-
43
- foreach ($this->clients as $name => $client) {
44
- [$errorCount, $traces] = $this->collectOnClient($client);
45
-
46
- $this->data['clients'][$name] = [
47
- 'traces' => $traces,
48
- 'error_count' => $errorCount,
49
- ];
50
-
51
- $this->data['request_count'] += \count($traces);
52
- $this->data['error_count'] += $errorCount;
53
- }
54
- }
55
-
56
- public function lateCollect()
57
- {
58
- foreach ($this->clients as $client) {
59
- $client->reset();
60
- }
61
- }
62
-
63
- public function getClients(): array
64
- {
65
- return $this->data['clients'] ?? [];
66
- }
67
-
68
- public function getRequestCount(): int
69
- {
70
- return $this->data['request_count'] ?? 0;
71
- }
72
-
73
- public function getErrorCount(): int
74
- {
75
- return $this->data['error_count'] ?? 0;
76
- }
77
-
78
- /**
79
- * {@inheritdoc}
80
- */
81
- public function getName(): string
82
- {
83
- return 'http_client';
84
- }
85
-
86
- public function reset()
87
- {
88
- $this->data = [
89
- 'clients' => [],
90
- 'request_count' => 0,
91
- 'error_count' => 0,
92
- ];
93
- }
94
-
95
- private function collectOnClient(TraceableHttpClient $client): array
96
- {
97
- $traces = $client->getTracedRequests();
98
- $errorCount = 0;
99
- $baseInfo = [
100
- 'response_headers' => 1,
101
- 'retry_count' => 1,
102
- 'redirect_count' => 1,
103
- 'redirect_url' => 1,
104
- 'user_data' => 1,
105
- 'error' => 1,
106
- 'url' => 1,
107
- ];
108
-
109
- foreach ($traces as $i => $trace) {
110
- if (400 <= ($trace['info']['http_code'] ?? 0)) {
111
- ++$errorCount;
112
- }
113
-
114
- $info = $trace['info'];
115
- $traces[$i]['http_code'] = $info['http_code'] ?? 0;
116
-
117
- unset($info['filetime'], $info['http_code'], $info['ssl_verify_result'], $info['content_type']);
118
-
119
- if (($info['http_method'] ?? null) === $trace['method']) {
120
- unset($info['http_method']);
121
- }
122
-
123
- if (($info['url'] ?? null) === $trace['url']) {
124
- unset($info['url']);
125
- }
126
-
127
- foreach ($info as $k => $v) {
128
- if (!$v || (is_numeric($v) && 0 > $v)) {
129
- unset($info[$k]);
130
- }
131
- }
132
-
133
- if (\is_string($content = $trace['content'])) {
134
- $contentType = 'application/octet-stream';
135
-
136
- foreach ($info['response_headers'] ?? [] as $h) {
137
- if (0 === stripos($h, 'content-type: ')) {
138
- $contentType = substr($h, \strlen('content-type: '));
139
- break;
140
- }
141
- }
142
-
143
- if (0 === strpos($contentType, 'image/') && class_exists(ImgStub::class)) {
144
- $content = new ImgStub($content, $contentType, '');
145
- } else {
146
- $content = [$content];
147
- }
148
-
149
- $content = ['response_content' => $content];
150
- } elseif (\is_array($content)) {
151
- $content = ['response_json' => $content];
152
- } else {
153
- $content = [];
154
- }
155
-
156
- if (isset($info['retry_count'])) {
157
- $content['retries'] = $info['previous_info'];
158
- unset($info['previous_info']);
159
- }
160
-
161
- $debugInfo = array_diff_key($info, $baseInfo);
162
- $info = ['info' => $debugInfo] + array_diff_key($info, $debugInfo) + $content;
163
- unset($traces[$i]['info']); // break PHP reference used by TraceableHttpClient
164
- $traces[$i]['info'] = $this->cloneVar($info);
165
- $traces[$i]['options'] = $this->cloneVar($trace['options']);
166
- }
167
-
168
- return [$errorCount, $traces];
169
- }
170
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/DecoratorTrait.php DELETED
@@ -1,66 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Symfony\Contracts\HttpClient\HttpClientInterface;
15
- use Symfony\Contracts\HttpClient\ResponseInterface;
16
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
17
- use Symfony\Contracts\Service\ResetInterface;
18
-
19
- /**
20
- * Eases with writing decorators.
21
- *
22
- * @author Nicolas Grekas <p@tchwork.com>
23
- */
24
- trait DecoratorTrait
25
- {
26
- private $client;
27
-
28
- public function __construct(HttpClientInterface $client = null)
29
- {
30
- $this->client = $client ?? HttpClient::create();
31
- }
32
-
33
- /**
34
- * {@inheritdoc}
35
- */
36
- public function request(string $method, string $url, array $options = []): ResponseInterface
37
- {
38
- return $this->client->request($method, $url, $options);
39
- }
40
-
41
- /**
42
- * {@inheritdoc}
43
- */
44
- public function stream($responses, float $timeout = null): ResponseStreamInterface
45
- {
46
- return $this->client->stream($responses, $timeout);
47
- }
48
-
49
- /**
50
- * {@inheritdoc}
51
- */
52
- public function withOptions(array $options): self
53
- {
54
- $clone = clone $this;
55
- $clone->client = $this->client->withOptions($options);
56
-
57
- return $clone;
58
- }
59
-
60
- public function reset()
61
- {
62
- if ($this->client instanceof ResetInterface) {
63
- $this->client->reset();
64
- }
65
- }
66
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/DependencyInjection/HttpClientPass.php DELETED
@@ -1,51 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\DependencyInjection;
13
-
14
- use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15
- use Symfony\Component\DependencyInjection\ContainerBuilder;
16
- use Symfony\Component\DependencyInjection\ContainerInterface;
17
- use Symfony\Component\DependencyInjection\Reference;
18
- use Symfony\Component\HttpClient\TraceableHttpClient;
19
-
20
- final class HttpClientPass implements CompilerPassInterface
21
- {
22
- private $clientTag;
23
-
24
- public function __construct(string $clientTag = 'http_client.client')
25
- {
26
- if (0 < \func_num_args()) {
27
- trigger_deprecation('symfony/http-client', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
28
- }
29
-
30
- $this->clientTag = $clientTag;
31
- }
32
-
33
- /**
34
- * {@inheritdoc}
35
- */
36
- public function process(ContainerBuilder $container)
37
- {
38
- if (!$container->hasDefinition('data_collector.http_client')) {
39
- return;
40
- }
41
-
42
- foreach ($container->findTaggedServiceIds($this->clientTag) as $id => $tags) {
43
- $container->register('.debug.'.$id, TraceableHttpClient::class)
44
- ->setArguments([new Reference('.debug.'.$id.'.inner'), new Reference('debug.stopwatch', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)])
45
- ->addTag('kernel.reset', ['method' => 'reset'])
46
- ->setDecoratedService($id);
47
- $container->getDefinition('data_collector.http_client')
48
- ->addMethodCall('registerClient', [$id, new Reference('.debug.'.$id)]);
49
- }
50
- }
51
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/EventSourceHttpClient.php DELETED
@@ -1,159 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
15
- use Symfony\Component\HttpClient\Exception\EventSourceException;
16
- use Symfony\Component\HttpClient\Response\AsyncContext;
17
- use Symfony\Component\HttpClient\Response\AsyncResponse;
18
- use Symfony\Contracts\HttpClient\ChunkInterface;
19
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
20
- use Symfony\Contracts\HttpClient\HttpClientInterface;
21
- use Symfony\Contracts\HttpClient\ResponseInterface;
22
- use Symfony\Contracts\Service\ResetInterface;
23
-
24
- /**
25
- * @author Antoine Bluchet <soyuka@gmail.com>
26
- * @author Nicolas Grekas <p@tchwork.com>
27
- */
28
- final class EventSourceHttpClient implements HttpClientInterface, ResetInterface
29
- {
30
- use AsyncDecoratorTrait, HttpClientTrait {
31
- AsyncDecoratorTrait::withOptions insteadof HttpClientTrait;
32
- }
33
-
34
- private $reconnectionTime;
35
-
36
- public function __construct(HttpClientInterface $client = null, float $reconnectionTime = 10.0)
37
- {
38
- $this->client = $client ?? HttpClient::create();
39
- $this->reconnectionTime = $reconnectionTime;
40
- }
41
-
42
- public function connect(string $url, array $options = []): ResponseInterface
43
- {
44
- return $this->request('GET', $url, self::mergeDefaultOptions($options, [
45
- 'buffer' => false,
46
- 'headers' => [
47
- 'Accept' => 'text/event-stream',
48
- 'Cache-Control' => 'no-cache',
49
- ],
50
- ], true));
51
- }
52
-
53
- public function request(string $method, string $url, array $options = []): ResponseInterface
54
- {
55
- $state = new class() {
56
- public $buffer = null;
57
- public $lastEventId = null;
58
- public $reconnectionTime;
59
- public $lastError = null;
60
- };
61
- $state->reconnectionTime = $this->reconnectionTime;
62
-
63
- if ($accept = self::normalizeHeaders($options['headers'] ?? [])['accept'] ?? []) {
64
- $state->buffer = \in_array($accept, [['Accept: text/event-stream'], ['accept: text/event-stream']], true) ? '' : null;
65
-
66
- if (null !== $state->buffer) {
67
- $options['extra']['trace_content'] = false;
68
- }
69
- }
70
-
71
- return new AsyncResponse($this->client, $method, $url, $options, static function (ChunkInterface $chunk, AsyncContext $context) use ($state, $method, $url, $options) {
72
- if (null !== $state->buffer) {
73
- $context->setInfo('reconnection_time', $state->reconnectionTime);
74
- $isTimeout = false;
75
- }
76
- $lastError = $state->lastError;
77
- $state->lastError = null;
78
-
79
- try {
80
- $isTimeout = $chunk->isTimeout();
81
-
82
- if (null !== $chunk->getInformationalStatus() || $context->getInfo('canceled')) {
83
- yield $chunk;
84
-
85
- return;
86
- }
87
- } catch (TransportExceptionInterface $e) {
88
- $state->lastError = $lastError ?? microtime(true);
89
-
90
- if (null === $state->buffer || ($isTimeout && microtime(true) - $state->lastError < $state->reconnectionTime)) {
91
- yield $chunk;
92
- } else {
93
- $options['headers']['Last-Event-ID'] = $state->lastEventId;
94
- $state->buffer = '';
95
- $state->lastError = microtime(true);
96
- $context->getResponse()->cancel();
97
- $context->replaceRequest($method, $url, $options);
98
- if ($isTimeout) {
99
- yield $chunk;
100
- } else {
101
- $context->pause($state->reconnectionTime);
102
- }
103
- }
104
-
105
- return;
106
- }
107
-
108
- if ($chunk->isFirst()) {
109
- if (preg_match('/^text\/event-stream(;|$)/i', $context->getHeaders()['content-type'][0] ?? '')) {
110
- $state->buffer = '';
111
- } elseif (null !== $lastError || (null !== $state->buffer && 200 === $context->getStatusCode())) {
112
- throw new EventSourceException(sprintf('Response content-type is "%s" while "text/event-stream" was expected for "%s".', $context->getHeaders()['content-type'][0] ?? '', $context->getInfo('url')));
113
- } else {
114
- $context->passthru();
115
- }
116
-
117
- if (null === $lastError) {
118
- yield $chunk;
119
- }
120
-
121
- return;
122
- }
123
-
124
- $rx = '/((?:\r\n|[\r\n]){2,})/';
125
- $content = $state->buffer.$chunk->getContent();
126
-
127
- if ($chunk->isLast()) {
128
- $rx = substr_replace($rx, '|$', -2, 0);
129
- }
130
- $events = preg_split($rx, $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
131
- $state->buffer = array_pop($events);
132
-
133
- for ($i = 0; isset($events[$i]); $i += 2) {
134
- $event = new ServerSentEvent($events[$i].$events[1 + $i]);
135
-
136
- if ('' !== $event->getId()) {
137
- $context->setInfo('last_event_id', $state->lastEventId = $event->getId());
138
- }
139
-
140
- if ($event->getRetry()) {
141
- $context->setInfo('reconnection_time', $state->reconnectionTime = $event->getRetry());
142
- }
143
-
144
- yield $event;
145
- }
146
-
147
- if (preg_match('/^(?::[^\r\n]*+(?:\r\n|[\r\n]))+$/m', $state->buffer)) {
148
- $content = $state->buffer;
149
- $state->buffer = '';
150
-
151
- yield $context->createChunk($content);
152
- }
153
-
154
- if ($chunk->isLast()) {
155
- yield $chunk;
156
- }
157
- });
158
- }
159
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Exception/ClientException.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
15
-
16
- /**
17
- * Represents a 4xx response.
18
- *
19
- * @author Nicolas Grekas <p@tchwork.com>
20
- */
21
- final class ClientException extends \RuntimeException implements ClientExceptionInterface
22
- {
23
- use HttpExceptionTrait;
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Exception/EventSourceException.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
15
-
16
- /**
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- final class EventSourceException extends \RuntimeException implements DecodingExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Exception/HttpExceptionTrait.php DELETED
@@ -1,78 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\ResponseInterface;
15
-
16
- /**
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- *
19
- * @internal
20
- */
21
- trait HttpExceptionTrait
22
- {
23
- private $response;
24
-
25
- public function __construct(ResponseInterface $response)
26
- {
27
- $this->response = $response;
28
- $code = $response->getInfo('http_code');
29
- $url = $response->getInfo('url');
30
- $message = sprintf('HTTP %d returned for "%s".', $code, $url);
31
-
32
- $httpCodeFound = false;
33
- $isJson = false;
34
- foreach (array_reverse($response->getInfo('response_headers')) as $h) {
35
- if (str_starts_with($h, 'HTTP/')) {
36
- if ($httpCodeFound) {
37
- break;
38
- }
39
-
40
- $message = sprintf('%s returned for "%s".', $h, $url);
41
- $httpCodeFound = true;
42
- }
43
-
44
- if (0 === stripos($h, 'content-type:')) {
45
- if (preg_match('/\bjson\b/i', $h)) {
46
- $isJson = true;
47
- }
48
-
49
- if ($httpCodeFound) {
50
- break;
51
- }
52
- }
53
- }
54
-
55
- // Try to guess a better error message using common API error formats
56
- // The MIME type isn't explicitly checked because some formats inherit from others
57
- // Ex: JSON:API follows RFC 7807 semantics, Hydra can be used in any JSON-LD-compatible format
58
- if ($isJson && $body = json_decode($response->getContent(false), true)) {
59
- if (isset($body['hydra:title']) || isset($body['hydra:description'])) {
60
- // see http://www.hydra-cg.com/spec/latest/core/#description-of-http-status-codes-and-errors
61
- $separator = isset($body['hydra:title'], $body['hydra:description']) ? "\n\n" : '';
62
- $message = ($body['hydra:title'] ?? '').$separator.($body['hydra:description'] ?? '');
63
- } elseif ((isset($body['title']) || isset($body['detail']))
64
- && (is_scalar($body['title'] ?? '') && is_scalar($body['detail'] ?? ''))) {
65
- // see RFC 7807 and https://jsonapi.org/format/#error-objects
66
- $separator = isset($body['title'], $body['detail']) ? "\n\n" : '';
67
- $message = ($body['title'] ?? '').$separator.($body['detail'] ?? '');
68
- }
69
- }
70
-
71
- parent::__construct($message, $code);
72
- }
73
-
74
- public function getResponse(): ResponseInterface
75
- {
76
- return $this->response;
77
- }
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Exception/InvalidArgumentException.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
15
-
16
- /**
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- final class InvalidArgumentException extends \InvalidArgumentException implements TransportExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Exception/JsonException.php DELETED
@@ -1,23 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
15
-
16
- /**
17
- * Thrown by responses' toArray() method when their content cannot be JSON-decoded.
18
- *
19
- * @author Nicolas Grekas <p@tchwork.com>
20
- */
21
- final class JsonException extends \JsonException implements DecodingExceptionInterface
22
- {
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Exception/RedirectionException.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
15
-
16
- /**
17
- * Represents a 3xx response.
18
- *
19
- * @author Nicolas Grekas <p@tchwork.com>
20
- */
21
- final class RedirectionException extends \RuntimeException implements RedirectionExceptionInterface
22
- {
23
- use HttpExceptionTrait;
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Exception/ServerException.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
15
-
16
- /**
17
- * Represents a 5xx response.
18
- *
19
- * @author Nicolas Grekas <p@tchwork.com>
20
- */
21
- final class ServerException extends \RuntimeException implements ServerExceptionInterface
22
- {
23
- use HttpExceptionTrait;
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Exception/TimeoutException.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\TimeoutExceptionInterface;
15
-
16
- /**
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- final class TimeoutException extends TransportException implements TimeoutExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Exception/TransportException.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Exception;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
15
-
16
- /**
17
- * @author Nicolas Grekas <p@tchwork.com>
18
- */
19
- class TransportException extends \RuntimeException implements TransportExceptionInterface
20
- {
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/HttpClient.php DELETED
@@ -1,78 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Amp\Http\Client\Connection\ConnectionLimitingPool;
15
- use Symfony\Contracts\HttpClient\HttpClientInterface;
16
-
17
- /**
18
- * A factory to instantiate the best possible HTTP client for the runtime.
19
- *
20
- * @author Nicolas Grekas <p@tchwork.com>
21
- */
22
- final class HttpClient
23
- {
24
- /**
25
- * @param array $defaultOptions Default request's options
26
- * @param int $maxHostConnections The maximum number of connections to a single host
27
- * @param int $maxPendingPushes The maximum number of pushed responses to accept in the queue
28
- *
29
- * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
30
- */
31
- public static function create(array $defaultOptions = [], int $maxHostConnections = 6, int $maxPendingPushes = 50): HttpClientInterface
32
- {
33
- if ($amp = class_exists(ConnectionLimitingPool::class)) {
34
- if (!\extension_loaded('curl')) {
35
- return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes);
36
- }
37
-
38
- // Skip curl when HTTP/2 push is unsupported or buggy, see https://bugs.php.net/77535
39
- if (\PHP_VERSION_ID < 70217 || (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70304) || !\defined('CURLMOPT_PUSHFUNCTION')) {
40
- return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes);
41
- }
42
-
43
- static $curlVersion = null;
44
- $curlVersion = $curlVersion ?? curl_version();
45
-
46
- // HTTP/2 push crashes before curl 7.61
47
- if (0x073D00 > $curlVersion['version_number'] || !(\CURL_VERSION_HTTP2 & $curlVersion['features'])) {
48
- return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes);
49
- }
50
- }
51
-
52
- if (\extension_loaded('curl')) {
53
- if ('\\' !== \DIRECTORY_SEPARATOR || isset($defaultOptions['cafile']) || isset($defaultOptions['capath']) || ini_get('curl.cainfo') || ini_get('openssl.cafile') || ini_get('openssl.capath')) {
54
- return new CurlHttpClient($defaultOptions, $maxHostConnections, $maxPendingPushes);
55
- }
56
-
57
- @trigger_error('Configure the "curl.cainfo", "openssl.cafile" or "openssl.capath" php.ini setting to enable the CurlHttpClient', \E_USER_WARNING);
58
- }
59
-
60
- if ($amp) {
61
- return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes);
62
- }
63
-
64
- @trigger_error((\extension_loaded('curl') ? 'Upgrade' : 'Install').' the curl extension or run "composer require amphp/http-client" to perform async HTTP operations, including full HTTP/2 support', \E_USER_NOTICE);
65
-
66
- return new NativeHttpClient($defaultOptions, $maxHostConnections);
67
- }
68
-
69
- /**
70
- * Creates a client that adds options (e.g. authentication headers) only when the request URL matches the provided base URI.
71
- */
72
- public static function createForBaseUri(string $baseUri, array $defaultOptions = [], int $maxHostConnections = 6, int $maxPendingPushes = 50): HttpClientInterface
73
- {
74
- $client = self::create([], $maxHostConnections, $maxPendingPushes);
75
-
76
- return ScopingHttpClient::forBaseUri($client, $baseUri, $defaultOptions);
77
- }
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/HttpClientTrait.php DELETED
@@ -1,684 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
15
- use Symfony\Component\HttpClient\Exception\TransportException;
16
-
17
- /**
18
- * Provides the common logic from writing HttpClientInterface implementations.
19
- *
20
- * All private methods are static to prevent implementers from creating memory leaks via circular references.
21
- *
22
- * @author Nicolas Grekas <p@tchwork.com>
23
- */
24
- trait HttpClientTrait
25
- {
26
- private static $CHUNK_SIZE = 16372;
27
-
28
- /**
29
- * {@inheritdoc}
30
- */
31
- public function withOptions(array $options): self
32
- {
33
- $clone = clone $this;
34
- $clone->defaultOptions = self::mergeDefaultOptions($options, $this->defaultOptions);
35
-
36
- return $clone;
37
- }
38
-
39
- /**
40
- * Validates and normalizes method, URL and options, and merges them with defaults.
41
- *
42
- * @throws InvalidArgumentException When a not-supported option is found
43
- */
44
- private static function prepareRequest(?string $method, ?string $url, array $options, array $defaultOptions = [], bool $allowExtraOptions = false): array
45
- {
46
- if (null !== $method) {
47
- if (\strlen($method) !== strspn($method, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')) {
48
- throw new InvalidArgumentException(sprintf('Invalid HTTP method "%s", only uppercase letters are accepted.', $method));
49
- }
50
- if (!$method) {
51
- throw new InvalidArgumentException('The HTTP method cannot be empty.');
52
- }
53
- }
54
-
55
- $options = self::mergeDefaultOptions($options, $defaultOptions, $allowExtraOptions);
56
-
57
- $buffer = $options['buffer'] ?? true;
58
-
59
- if ($buffer instanceof \Closure) {
60
- $options['buffer'] = static function (array $headers) use ($buffer) {
61
- if (!\is_bool($buffer = $buffer($headers))) {
62
- if (!\is_array($bufferInfo = @stream_get_meta_data($buffer))) {
63
- throw new \LogicException(sprintf('The closure passed as option "buffer" must return bool or stream resource, got "%s".', get_debug_type($buffer)));
64
- }
65
-
66
- if (false === strpbrk($bufferInfo['mode'], 'acew+')) {
67
- throw new \LogicException(sprintf('The stream returned by the closure passed as option "buffer" must be writeable, got mode "%s".', $bufferInfo['mode']));
68
- }
69
- }
70
-
71
- return $buffer;
72
- };
73
- } elseif (!\is_bool($buffer)) {
74
- if (!\is_array($bufferInfo = @stream_get_meta_data($buffer))) {
75
- throw new InvalidArgumentException(sprintf('Option "buffer" must be bool, stream resource or Closure, "%s" given.', get_debug_type($buffer)));
76
- }
77
-
78
- if (false === strpbrk($bufferInfo['mode'], 'acew+')) {
79
- throw new InvalidArgumentException(sprintf('The stream in option "buffer" must be writeable, mode "%s" given.', $bufferInfo['mode']));
80
- }
81
- }
82
-
83
- if (isset($options['json'])) {
84
- if (isset($options['body']) && '' !== $options['body']) {
85
- throw new InvalidArgumentException('Define either the "json" or the "body" option, setting both is not supported.');
86
- }
87
- $options['body'] = self::jsonEncode($options['json']);
88
- unset($options['json']);
89
-
90
- if (!isset($options['normalized_headers']['content-type'])) {
91
- $options['normalized_headers']['content-type'] = ['Content-Type: application/json'];
92
- }
93
- }
94
-
95
- if (!isset($options['normalized_headers']['accept'])) {
96
- $options['normalized_headers']['accept'] = ['Accept: */*'];
97
- }
98
-
99
- if (isset($options['body'])) {
100
- $options['body'] = self::normalizeBody($options['body']);
101
-
102
- if (\is_string($options['body'])
103
- && (string) \strlen($options['body']) !== substr($h = $options['normalized_headers']['content-length'][0] ?? '', 16)
104
- && ('' !== $h || '' !== $options['body'])
105
- ) {
106
- if ('chunked' === substr($options['normalized_headers']['transfer-encoding'][0] ?? '', \strlen('Transfer-Encoding: '))) {
107
- unset($options['normalized_headers']['transfer-encoding']);
108
- $options['body'] = self::dechunk($options['body']);
109
- }
110
-
111
- $options['normalized_headers']['content-length'] = [substr_replace($h ?: 'Content-Length: ', \strlen($options['body']), 16)];
112
- }
113
- }
114
-
115
- if (isset($options['peer_fingerprint'])) {
116
- $options['peer_fingerprint'] = self::normalizePeerFingerprint($options['peer_fingerprint']);
117
- }
118
-
119
- // Validate on_progress
120
- if (!\is_callable($onProgress = $options['on_progress'] ?? 'var_dump')) {
121
- throw new InvalidArgumentException(sprintf('Option "on_progress" must be callable, "%s" given.', get_debug_type($onProgress)));
122
- }
123
-
124
- if (\is_array($options['auth_basic'] ?? null)) {
125
- $count = \count($options['auth_basic']);
126
- if ($count <= 0 || $count > 2) {
127
- throw new InvalidArgumentException(sprintf('Option "auth_basic" must contain 1 or 2 elements, "%s" given.', $count));
128
- }
129
-
130
- $options['auth_basic'] = implode(':', $options['auth_basic']);
131
- }
132
-
133
- if (!\is_string($options['auth_basic'] ?? '')) {
134
- throw new InvalidArgumentException(sprintf('Option "auth_basic" must be string or an array, "%s" given.', get_debug_type($options['auth_basic'])));
135
- }
136
-
137
- if (isset($options['auth_bearer'])) {
138
- if (!\is_string($options['auth_bearer'])) {
139
- throw new InvalidArgumentException(sprintf('Option "auth_bearer" must be a string, "%s" given.', get_debug_type($options['auth_bearer'])));
140
- }
141
- if (preg_match('{[^\x21-\x7E]}', $options['auth_bearer'])) {
142
- throw new InvalidArgumentException('Invalid character found in option "auth_bearer": '.json_encode($options['auth_bearer']).'.');
143
- }
144
- }
145
-
146
- if (isset($options['auth_basic'], $options['auth_bearer'])) {
147
- throw new InvalidArgumentException('Define either the "auth_basic" or the "auth_bearer" option, setting both is not supported.');
148
- }
149
-
150
- if (null !== $url) {
151
- // Merge auth with headers
152
- if (($options['auth_basic'] ?? false) && !($options['normalized_headers']['authorization'] ?? false)) {
153
- $options['normalized_headers']['authorization'] = ['Authorization: Basic '.base64_encode($options['auth_basic'])];
154
- }
155
- // Merge bearer with headers
156
- if (($options['auth_bearer'] ?? false) && !($options['normalized_headers']['authorization'] ?? false)) {
157
- $options['normalized_headers']['authorization'] = ['Authorization: Bearer '.$options['auth_bearer']];
158
- }
159
-
160
- unset($options['auth_basic'], $options['auth_bearer']);
161
-
162
- // Parse base URI
163
- if (\is_string($options['base_uri'])) {
164
- $options['base_uri'] = self::parseUrl($options['base_uri']);
165
- }
166
-
167
- // Validate and resolve URL
168
- $url = self::parseUrl($url, $options['query']);
169
- $url = self::resolveUrl($url, $options['base_uri'], $defaultOptions['query'] ?? []);
170
- }
171
-
172
- // Finalize normalization of options
173
- $options['http_version'] = (string) ($options['http_version'] ?? '') ?: null;
174
- if (0 > $options['timeout'] = (float) ($options['timeout'] ?? ini_get('default_socket_timeout'))) {
175
- $options['timeout'] = 172800.0; // 2 days
176
- }
177
-
178
- $options['max_duration'] = isset($options['max_duration']) ? (float) $options['max_duration'] : 0;
179
- $options['headers'] = array_merge(...array_values($options['normalized_headers']));
180
-
181
- return [$url, $options];
182
- }
183
-
184
- /**
185
- * @throws InvalidArgumentException When an invalid option is found
186
- */
187
- private static function mergeDefaultOptions(array $options, array $defaultOptions, bool $allowExtraOptions = false): array
188
- {
189
- $options['normalized_headers'] = self::normalizeHeaders($options['headers'] ?? []);
190
-
191
- if ($defaultOptions['headers'] ?? false) {
192
- $options['normalized_headers'] += self::normalizeHeaders($defaultOptions['headers']);
193
- }
194
-
195
- $options['headers'] = array_merge(...array_values($options['normalized_headers']) ?: [[]]);
196
-
197
- if ($resolve = $options['resolve'] ?? false) {
198
- $options['resolve'] = [];
199
- foreach ($resolve as $k => $v) {
200
- $options['resolve'][substr(self::parseUrl('http://'.$k)['authority'], 2)] = (string) $v;
201
- }
202
- }
203
-
204
- // Option "query" is never inherited from defaults
205
- $options['query'] = $options['query'] ?? [];
206
-
207
- $options += $defaultOptions;
208
-
209
- foreach (self::$emptyDefaults ?? [] as $k => $v) {
210
- if (!isset($options[$k])) {
211
- $options[$k] = $v;
212
- }
213
- }
214
-
215
- if (isset($defaultOptions['extra'])) {
216
- $options['extra'] += $defaultOptions['extra'];
217
- }
218
-
219
- if ($resolve = $defaultOptions['resolve'] ?? false) {
220
- foreach ($resolve as $k => $v) {
221
- $options['resolve'] += [substr(self::parseUrl('http://'.$k)['authority'], 2) => (string) $v];
222
- }
223
- }
224
-
225
- if ($allowExtraOptions || !$defaultOptions) {
226
- return $options;
227
- }
228
-
229
- // Look for unsupported options
230
- foreach ($options as $name => $v) {
231
- if (\array_key_exists($name, $defaultOptions) || 'normalized_headers' === $name) {
232
- continue;
233
- }
234
-
235
- if ('auth_ntlm' === $name) {
236
- if (!\extension_loaded('curl')) {
237
- $msg = 'try installing the "curl" extension to use "%s" instead.';
238
- } else {
239
- $msg = 'try using "%s" instead.';
240
- }
241
-
242
- throw new InvalidArgumentException(sprintf('Option "auth_ntlm" is not supported by "%s", '.$msg, __CLASS__, CurlHttpClient::class));
243
- }
244
-
245
- $alternatives = [];
246
-
247
- foreach ($defaultOptions as $k => $v) {
248
- if (levenshtein($name, $k) <= \strlen($name) / 3 || str_contains($k, $name)) {
249
- $alternatives[] = $k;
250
- }
251
- }
252
-
253
- throw new InvalidArgumentException(sprintf('Unsupported option "%s" passed to "%s", did you mean "%s"?', $name, __CLASS__, implode('", "', $alternatives ?: array_keys($defaultOptions))));
254
- }
255
-
256
- return $options;
257
- }
258
-
259
- /**
260
- * @return string[][]
261
- *
262
- * @throws InvalidArgumentException When an invalid header is found
263
- */
264
- private static function normalizeHeaders(array $headers): array
265
- {
266
- $normalizedHeaders = [];
267
-
268
- foreach ($headers as $name => $values) {
269
- if (\is_object($values) && method_exists($values, '__toString')) {
270
- $values = (string) $values;
271
- }
272
-
273
- if (\is_int($name)) {
274
- if (!\is_string($values)) {
275
- throw new InvalidArgumentException(sprintf('Invalid value for header "%s": expected string, "%s" given.', $name, get_debug_type($values)));
276
- }
277
- [$name, $values] = explode(':', $values, 2);
278
- $values = [ltrim($values)];
279
- } elseif (!is_iterable($values)) {
280
- if (\is_object($values)) {
281
- throw new InvalidArgumentException(sprintf('Invalid value for header "%s": expected string, "%s" given.', $name, get_debug_type($values)));
282
- }
283
-
284
- $values = (array) $values;
285
- }
286
-
287
- $lcName = strtolower($name);
288
- $normalizedHeaders[$lcName] = [];
289
-
290
- foreach ($values as $value) {
291
- $normalizedHeaders[$lcName][] = $value = $name.': '.$value;
292
-
293
- if (\strlen($value) !== strcspn($value, "\r\n\0")) {
294
- throw new InvalidArgumentException(sprintf('Invalid header: CR/LF/NUL found in "%s".', $value));
295
- }
296
- }
297
- }
298
-
299
- return $normalizedHeaders;
300
- }
301
-
302
- /**
303
- * @param array|string|resource|\Traversable|\Closure $body
304
- *
305
- * @return string|resource|\Closure
306
- *
307
- * @throws InvalidArgumentException When an invalid body is passed
308
- */
309
- private static function normalizeBody($body)
310
- {
311
- if (\is_array($body)) {
312
- array_walk_recursive($body, $caster = static function (&$v) use (&$caster) {
313
- if (\is_object($v)) {
314
- if ($vars = get_object_vars($v)) {
315
- array_walk_recursive($vars, $caster);
316
- $v = $vars;
317
- } elseif (method_exists($v, '__toString')) {
318
- $v = (string) $v;
319
- }
320
- }
321
- });
322
-
323
- return http_build_query($body, '', '&');
324
- }
325
-
326
- if (\is_string($body)) {
327
- return $body;
328
- }
329
-
330
- $generatorToCallable = static function (\Generator $body): \Closure {
331
- return static function () use ($body) {
332
- while ($body->valid()) {
333
- $chunk = $body->current();
334
- $body->next();
335
-
336
- if ('' !== $chunk) {
337
- return $chunk;
338
- }
339
- }
340
-
341
- return '';
342
- };
343
- };
344
-
345
- if ($body instanceof \Generator) {
346
- return $generatorToCallable($body);
347
- }
348
-
349
- if ($body instanceof \Traversable) {
350
- return $generatorToCallable((static function ($body) { yield from $body; })($body));
351
- }
352
-
353
- if ($body instanceof \Closure) {
354
- $r = new \ReflectionFunction($body);
355
- $body = $r->getClosure();
356
-
357
- if ($r->isGenerator()) {
358
- $body = $body(self::$CHUNK_SIZE);
359
-
360
- return $generatorToCallable($body);
361
- }
362
-
363
- return $body;
364
- }
365
-
366
- if (!\is_array(@stream_get_meta_data($body))) {
367
- throw new InvalidArgumentException(sprintf('Option "body" must be string, stream resource, iterable or callable, "%s" given.', get_debug_type($body)));
368
- }
369
-
370
- return $body;
371
- }
372
-
373
- private static function dechunk(string $body): string
374
- {
375
- $h = fopen('php://temp', 'w+');
376
- stream_filter_append($h, 'dechunk', \STREAM_FILTER_WRITE);
377
- fwrite($h, $body);
378
- $body = stream_get_contents($h, -1, 0);
379
- rewind($h);
380
- ftruncate($h, 0);
381
-
382
- if (fwrite($h, '-') && '' !== stream_get_contents($h, -1, 0)) {
383
- throw new TransportException('Request body has broken chunked encoding.');
384
- }
385
-
386
- return $body;
387
- }
388
-
389
- /**
390
- * @param string|string[] $fingerprint
391
- *
392
- * @throws InvalidArgumentException When an invalid fingerprint is passed
393
- */
394
- private static function normalizePeerFingerprint($fingerprint): array
395
- {
396
- if (\is_string($fingerprint)) {
397
- switch (\strlen($fingerprint = str_replace(':', '', $fingerprint))) {
398
- case 32: $fingerprint = ['md5' => $fingerprint]; break;
399
- case 40: $fingerprint = ['sha1' => $fingerprint]; break;
400
- case 44: $fingerprint = ['pin-sha256' => [$fingerprint]]; break;
401
- case 64: $fingerprint = ['sha256' => $fingerprint]; break;
402
- default: throw new InvalidArgumentException(sprintf('Cannot auto-detect fingerprint algorithm for "%s".', $fingerprint));
403
- }
404
- } elseif (\is_array($fingerprint)) {
405
- foreach ($fingerprint as $algo => $hash) {
406
- $fingerprint[$algo] = 'pin-sha256' === $algo ? (array) $hash : str_replace(':', '', $hash);
407
- }
408
- } else {
409
- throw new InvalidArgumentException(sprintf('Option "peer_fingerprint" must be string or array, "%s" given.', get_debug_type($fingerprint)));
410
- }
411
-
412
- return $fingerprint;
413
- }
414
-
415
- /**
416
- * @param mixed $value
417
- *
418
- * @throws InvalidArgumentException When the value cannot be json-encoded
419
- */
420
- private static function jsonEncode($value, int $flags = null, int $maxDepth = 512): string
421
- {
422
- $flags = $flags ?? (\JSON_HEX_TAG | \JSON_HEX_APOS | \JSON_HEX_AMP | \JSON_HEX_QUOT | \JSON_PRESERVE_ZERO_FRACTION);
423
-
424
- try {
425
- $value = json_encode($value, $flags | (\PHP_VERSION_ID >= 70300 ? \JSON_THROW_ON_ERROR : 0), $maxDepth);
426
- } catch (\JsonException $e) {
427
- throw new InvalidArgumentException('Invalid value for "json" option: '.$e->getMessage());
428
- }
429
-
430
- if (\PHP_VERSION_ID < 70300 && \JSON_ERROR_NONE !== json_last_error() && (false === $value || !($flags & \JSON_PARTIAL_OUTPUT_ON_ERROR))) {
431
- throw new InvalidArgumentException('Invalid value for "json" option: '.json_last_error_msg());
432
- }
433
-
434
- return $value;
435
- }
436
-
437
- /**
438
- * Resolves a URL against a base URI.
439
- *
440
- * @see https://tools.ietf.org/html/rfc3986#section-5.2.2
441
- *
442
- * @throws InvalidArgumentException When an invalid URL is passed
443
- */
444
- private static function resolveUrl(array $url, ?array $base, array $queryDefaults = []): array
445
- {
446
- if (null !== $base && '' === ($base['scheme'] ?? '').($base['authority'] ?? '')) {
447
- throw new InvalidArgumentException(sprintf('Invalid "base_uri" option: host or scheme is missing in "%s".', implode('', $base)));
448
- }
449
-
450
- if (null === $url['scheme'] && (null === $base || null === $base['scheme'])) {
451
- throw new InvalidArgumentException(sprintf('Invalid URL: scheme is missing in "%s". Did you forget to add "http(s)://"?', implode('', $base ?? $url)));
452
- }
453
-
454
- if (null === $base && '' === $url['scheme'].$url['authority']) {
455
- throw new InvalidArgumentException(sprintf('Invalid URL: no "base_uri" option was provided and host or scheme is missing in "%s".', implode('', $url)));
456
- }
457
-
458
- if (null !== $url['scheme']) {
459
- $url['path'] = self::removeDotSegments($url['path'] ?? '');
460
- } else {
461
- if (null !== $url['authority']) {
462
- $url['path'] = self::removeDotSegments($url['path'] ?? '');
463
- } else {
464
- if (null === $url['path']) {
465
- $url['path'] = $base['path'];
466
- $url['query'] = $url['query'] ?? $base['query'];
467
- } else {
468
- if ('/' !== $url['path'][0]) {
469
- if (null === $base['path']) {
470
- $url['path'] = '/'.$url['path'];
471
- } else {
472
- $segments = explode('/', $base['path']);
473
- array_splice($segments, -1, 1, [$url['path']]);
474
- $url['path'] = implode('/', $segments);
475
- }
476
- }
477
-
478
- $url['path'] = self::removeDotSegments($url['path']);
479
- }
480
-
481
- $url['authority'] = $base['authority'];
482
-
483
- if ($queryDefaults) {
484
- $url['query'] = '?'.self::mergeQueryString(substr($url['query'] ?? '', 1), $queryDefaults, false);
485
- }
486
- }
487
-
488
- $url['scheme'] = $base['scheme'];
489
- }
490
-
491
- if ('' === ($url['path'] ?? '')) {
492
- $url['path'] = '/';
493
- }
494
-
495
- if ('?' === ($url['query'] ?? '')) {
496
- $url['query'] = null;
497
- }
498
-
499
- return $url;
500
- }
501
-
502
- /**
503
- * Parses a URL and fixes its encoding if needed.
504
- *
505
- * @throws InvalidArgumentException When an invalid URL is passed
506
- */
507
- private static function parseUrl(string $url, array $query = [], array $allowedSchemes = ['http' => 80, 'https' => 443]): array
508
- {
509
- if (false === $parts = parse_url($url)) {
510
- throw new InvalidArgumentException(sprintf('Malformed URL "%s".', $url));
511
- }
512
-
513
- if ($query) {
514
- $parts['query'] = self::mergeQueryString($parts['query'] ?? null, $query, true);
515
- }
516
-
517
- $port = $parts['port'] ?? 0;
518
-
519
- if (null !== $scheme = $parts['scheme'] ?? null) {
520
- if (!isset($allowedSchemes[$scheme = strtolower($scheme)])) {
521
- throw new InvalidArgumentException(sprintf('Unsupported scheme in "%s".', $url));
522
- }
523
-
524
- $port = $allowedSchemes[$scheme] === $port ? 0 : $port;
525
- $scheme .= ':';
526
- }
527
-
528
- if (null !== $host = $parts['host'] ?? null) {
529
- if (!\defined('INTL_IDNA_VARIANT_UTS46') && preg_match('/[\x80-\xFF]/', $host)) {
530
- throw new InvalidArgumentException(sprintf('Unsupported IDN "%s", try enabling the "intl" PHP extension or running "composer require symfony/polyfill-intl-idn".', $host));
531
- }
532
-
533
- $host = \defined('INTL_IDNA_VARIANT_UTS46') ? idn_to_ascii($host, \IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI | \IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII, \INTL_IDNA_VARIANT_UTS46) ?: strtolower($host) : strtolower($host);
534
- $host .= $port ? ':'.$port : '';
535
- }
536
-
537
- foreach (['user', 'pass', 'path', 'query', 'fragment'] as $part) {
538
- if (!isset($parts[$part])) {
539
- continue;
540
- }
541
-
542
- if (str_contains($parts[$part], '%')) {
543
- // https://tools.ietf.org/html/rfc3986#section-2.3
544
- $parts[$part] = preg_replace_callback('/%(?:2[DE]|3[0-9]|[46][1-9A-F]|5F|[57][0-9A]|7E)++/i', function ($m) { return rawurldecode($m[0]); }, $parts[$part]);
545
- }
546
-
547
- // https://tools.ietf.org/html/rfc3986#section-3.3
548
- $parts[$part] = preg_replace_callback("#[^-A-Za-z0-9._~!$&/'()*+,;=:@%]++#", function ($m) { return rawurlencode($m[0]); }, $parts[$part]);
549
- }
550
-
551
- return [
552
- 'scheme' => $scheme,
553
- 'authority' => null !== $host ? '//'.(isset($parts['user']) ? $parts['user'].(isset($parts['pass']) ? ':'.$parts['pass'] : '').'@' : '').$host : null,
554
- 'path' => isset($parts['path'][0]) ? $parts['path'] : null,
555
- 'query' => isset($parts['query']) ? '?'.$parts['query'] : null,
556
- 'fragment' => isset($parts['fragment']) ? '#'.$parts['fragment'] : null,
557
- ];
558
- }
559
-
560
- /**
561
- * Removes dot-segments from a path.
562
- *
563
- * @see https://tools.ietf.org/html/rfc3986#section-5.2.4
564
- */
565
- private static function removeDotSegments(string $path)
566
- {
567
- $result = '';
568
-
569
- while (!\in_array($path, ['', '.', '..'], true)) {
570
- if ('.' === $path[0] && (str_starts_with($path, $p = '../') || str_starts_with($path, $p = './'))) {
571
- $path = substr($path, \strlen($p));
572
- } elseif ('/.' === $path || str_starts_with($path, '/./')) {
573
- $path = substr_replace($path, '/', 0, 3);
574
- } elseif ('/..' === $path || str_starts_with($path, '/../')) {
575
- $i = strrpos($result, '/');
576
- $result = $i ? substr($result, 0, $i) : '';
577
- $path = substr_replace($path, '/', 0, 4);
578
- } else {
579
- $i = strpos($path, '/', 1) ?: \strlen($path);
580
- $result .= substr($path, 0, $i);
581
- $path = substr($path, $i);
582
- }
583
- }
584
-
585
- return $result;
586
- }
587
-
588
- /**
589
- * Merges and encodes a query array with a query string.
590
- *
591
- * @throws InvalidArgumentException When an invalid query-string value is passed
592
- */
593
- private static function mergeQueryString(?string $queryString, array $queryArray, bool $replace): ?string
594
- {
595
- if (!$queryArray) {
596
- return $queryString;
597
- }
598
-
599
- $query = [];
600
-
601
- if (null !== $queryString) {
602
- foreach (explode('&', $queryString) as $v) {
603
- if ('' !== $v) {
604
- $k = urldecode(explode('=', $v, 2)[0]);
605
- $query[$k] = (isset($query[$k]) ? $query[$k].'&' : '').$v;
606
- }
607
- }
608
- }
609
-
610
- if ($replace) {
611
- foreach ($queryArray as $k => $v) {
612
- if (null === $v) {
613
- unset($query[$k]);
614
- }
615
- }
616
- }
617
-
618
- $queryString = http_build_query($queryArray, '', '&', \PHP_QUERY_RFC3986);
619
- $queryArray = [];
620
-
621
- if ($queryString) {
622
- foreach (explode('&', $queryString) as $v) {
623
- $queryArray[rawurldecode(explode('=', $v, 2)[0])] = $v;
624
- }
625
- }
626
-
627
- return implode('&', $replace ? array_replace($query, $queryArray) : ($query + $queryArray));
628
- }
629
-
630
- /**
631
- * Loads proxy configuration from the same environment variables as curl when no proxy is explicitly set.
632
- */
633
- private static function getProxy(?string $proxy, array $url, ?string $noProxy): ?array
634
- {
635
- if (null === $proxy) {
636
- // Ignore HTTP_PROXY except on the CLI to work around httpoxy set of vulnerabilities
637
- $proxy = $_SERVER['http_proxy'] ?? (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? $_SERVER['HTTP_PROXY'] ?? null : null) ?? $_SERVER['all_proxy'] ?? $_SERVER['ALL_PROXY'] ?? null;
638
-
639
- if ('https:' === $url['scheme']) {
640
- $proxy = $_SERVER['https_proxy'] ?? $_SERVER['HTTPS_PROXY'] ?? $proxy;
641
- }
642
- }
643
-
644
- if (null === $proxy) {
645
- return null;
646
- }
647
-
648
- $proxy = (parse_url($proxy) ?: []) + ['scheme' => 'http'];
649
-
650
- if (!isset($proxy['host'])) {
651
- throw new TransportException('Invalid HTTP proxy: host is missing.');
652
- }
653
-
654
- if ('http' === $proxy['scheme']) {
655
- $proxyUrl = 'tcp://'.$proxy['host'].':'.($proxy['port'] ?? '80');
656
- } elseif ('https' === $proxy['scheme']) {
657
- $proxyUrl = 'ssl://'.$proxy['host'].':'.($proxy['port'] ?? '443');
658
- } else {
659
- throw new TransportException(sprintf('Unsupported proxy scheme "%s": "http" or "https" expected.', $proxy['scheme']));
660
- }
661
-
662
- $noProxy = $noProxy ?? $_SERVER['no_proxy'] ?? $_SERVER['NO_PROXY'] ?? '';
663
- $noProxy = $noProxy ? preg_split('/[\s,]+/', $noProxy) : [];
664
-
665
- return [
666
- 'url' => $proxyUrl,
667
- 'auth' => isset($proxy['user']) ? 'Basic '.base64_encode(rawurldecode($proxy['user']).':'.rawurldecode($proxy['pass'] ?? '')) : null,
668
- 'no_proxy' => $noProxy,
669
- ];
670
- }
671
-
672
- private static function shouldBuffer(array $headers): bool
673
- {
674
- if (null === $contentType = $headers['content-type'][0] ?? null) {
675
- return false;
676
- }
677
-
678
- if (false !== $i = strpos($contentType, ';')) {
679
- $contentType = substr($contentType, 0, $i);
680
- }
681
-
682
- return $contentType && preg_match('#^(?:text/|application/(?:.+\+)?(?:json|xml)$)#i', $contentType);
683
- }
684
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/HttpOptions.php DELETED
@@ -1,331 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Symfony\Contracts\HttpClient\HttpClientInterface;
15
-
16
- /**
17
- * A helper providing autocompletion for available options.
18
- *
19
- * @see HttpClientInterface for a description of each options.
20
- *
21
- * @author Nicolas Grekas <p@tchwork.com>
22
- */
23
- class HttpOptions
24
- {
25
- private $options = [];
26
-
27
- public function toArray(): array
28
- {
29
- return $this->options;
30
- }
31
-
32
- /**
33
- * @return $this
34
- */
35
- public function setAuthBasic(string $user, string $password = '')
36
- {
37
- $this->options['auth_basic'] = $user;
38
-
39
- if ('' !== $password) {
40
- $this->options['auth_basic'] .= ':'.$password;
41
- }
42
-
43
- return $this;
44
- }
45
-
46
- /**
47
- * @return $this
48
- */
49
- public function setAuthBearer(string $token)
50
- {
51
- $this->options['auth_bearer'] = $token;
52
-
53
- return $this;
54
- }
55
-
56
- /**
57
- * @return $this
58
- */
59
- public function setQuery(array $query)
60
- {
61
- $this->options['query'] = $query;
62
-
63
- return $this;
64
- }
65
-
66
- /**
67
- * @return $this
68
- */
69
- public function setHeaders(iterable $headers)
70
- {
71
- $this->options['headers'] = $headers;
72
-
73
- return $this;
74
- }
75
-
76
- /**
77
- * @param array|string|resource|\Traversable|\Closure $body
78
- *
79
- * @return $this
80
- */
81
- public function setBody($body)
82
- {
83
- $this->options['body'] = $body;
84
-
85
- return $this;
86
- }
87
-
88
- /**
89
- * @param mixed $json
90
- *
91
- * @return $this
92
- */
93
- public function setJson($json)
94
- {
95
- $this->options['json'] = $json;
96
-
97
- return $this;
98
- }
99
-
100
- /**
101
- * @return $this
102
- */
103
- public function setUserData($data)
104
- {
105
- $this->options['user_data'] = $data;
106
-
107
- return $this;
108
- }
109
-
110
- /**
111
- * @return $this
112
- */
113
- public function setMaxRedirects(int $max)
114
- {
115
- $this->options['max_redirects'] = $max;
116
-
117
- return $this;
118
- }
119
-
120
- /**
121
- * @return $this
122
- */
123
- public function setHttpVersion(string $version)
124
- {
125
- $this->options['http_version'] = $version;
126
-
127
- return $this;
128
- }
129
-
130
- /**
131
- * @return $this
132
- */
133
- public function setBaseUri(string $uri)
134
- {
135
- $this->options['base_uri'] = $uri;
136
-
137
- return $this;
138
- }
139
-
140
- /**
141
- * @return $this
142
- */
143
- public function buffer(bool $buffer)
144
- {
145
- $this->options['buffer'] = $buffer;
146
-
147
- return $this;
148
- }
149
-
150
- /**
151
- * @return $this
152
- */
153
- public function setOnProgress(callable $callback)
154
- {
155
- $this->options['on_progress'] = $callback;
156
-
157
- return $this;
158
- }
159
-
160
- /**
161
- * @return $this
162
- */
163
- public function resolve(array $hostIps)
164
- {
165
- $this->options['resolve'] = $hostIps;
166
-
167
- return $this;
168
- }
169
-
170
- /**
171
- * @return $this
172
- */
173
- public function setProxy(string $proxy)
174
- {
175
- $this->options['proxy'] = $proxy;
176
-
177
- return $this;
178
- }
179
-
180
- /**
181
- * @return $this
182
- */
183
- public function setNoProxy(string $noProxy)
184
- {
185
- $this->options['no_proxy'] = $noProxy;
186
-
187
- return $this;
188
- }
189
-
190
- /**
191
- * @return $this
192
- */
193
- public function setTimeout(float $timeout)
194
- {
195
- $this->options['timeout'] = $timeout;
196
-
197
- return $this;
198
- }
199
-
200
- /**
201
- * @return $this
202
- */
203
- public function setMaxDuration(float $maxDuration)
204
- {
205
- $this->options['max_duration'] = $maxDuration;
206
-
207
- return $this;
208
- }
209
-
210
- /**
211
- * @return $this
212
- */
213
- public function bindTo(string $bindto)
214
- {
215
- $this->options['bindto'] = $bindto;
216
-
217
- return $this;
218
- }
219
-
220
- /**
221
- * @return $this
222
- */
223
- public function verifyPeer(bool $verify)
224
- {
225
- $this->options['verify_peer'] = $verify;
226
-
227
- return $this;
228
- }
229
-
230
- /**
231
- * @return $this
232
- */
233
- public function verifyHost(bool $verify)
234
- {
235
- $this->options['verify_host'] = $verify;
236
-
237
- return $this;
238
- }
239
-
240
- /**
241
- * @return $this
242
- */
243
- public function setCaFile(string $cafile)
244
- {
245
- $this->options['cafile'] = $cafile;
246
-
247
- return $this;
248
- }
249
-
250
- /**
251
- * @return $this
252
- */
253
- public function setCaPath(string $capath)
254
- {
255
- $this->options['capath'] = $capath;
256
-
257
- return $this;
258
- }
259
-
260
- /**
261
- * @return $this
262
- */
263
- public function setLocalCert(string $cert)
264
- {
265
- $this->options['local_cert'] = $cert;
266
-
267
- return $this;
268
- }
269
-
270
- /**
271
- * @return $this
272
- */
273
- public function setLocalPk(string $pk)
274
- {
275
- $this->options['local_pk'] = $pk;
276
-
277
- return $this;
278
- }
279
-
280
- /**
281
- * @return $this
282
- */
283
- public function setPassphrase(string $passphrase)
284
- {
285
- $this->options['passphrase'] = $passphrase;
286
-
287
- return $this;
288
- }
289
-
290
- /**
291
- * @return $this
292
- */
293
- public function setCiphers(string $ciphers)
294
- {
295
- $this->options['ciphers'] = $ciphers;
296
-
297
- return $this;
298
- }
299
-
300
- /**
301
- * @param string|array $fingerprint
302
- *
303
- * @return $this
304
- */
305
- public function setPeerFingerprint($fingerprint)
306
- {
307
- $this->options['peer_fingerprint'] = $fingerprint;
308
-
309
- return $this;
310
- }
311
-
312
- /**
313
- * @return $this
314
- */
315
- public function capturePeerCertChain(bool $capture)
316
- {
317
- $this->options['capture_peer_cert_chain'] = $capture;
318
-
319
- return $this;
320
- }
321
-
322
- /**
323
- * @return $this
324
- */
325
- public function setExtra(string $name, $value)
326
- {
327
- $this->options['extra'][$name] = $value;
328
-
329
- return $this;
330
- }
331
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/HttplugClient.php DELETED
@@ -1,271 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use GuzzleHttp\Promise\Promise as GuzzlePromise;
15
- use GuzzleHttp\Promise\RejectedPromise;
16
- use GuzzleHttp\Promise\Utils;
17
- use Http\Client\Exception\NetworkException;
18
- use Http\Client\Exception\RequestException;
19
- use Http\Client\HttpAsyncClient;
20
- use Http\Client\HttpClient as HttplugInterface;
21
- use Http\Discovery\Exception\NotFoundException;
22
- use Http\Discovery\Psr17FactoryDiscovery;
23
- use Http\Message\RequestFactory;
24
- use Http\Message\StreamFactory;
25
- use Http\Message\UriFactory;
26
- use Http\Promise\Promise;
27
- use Nyholm\Psr7\Factory\Psr17Factory;
28
- use Nyholm\Psr7\Request;
29
- use Nyholm\Psr7\Uri;
30
- use Psr\Http\Message\RequestFactoryInterface;
31
- use Psr\Http\Message\RequestInterface;
32
- use Psr\Http\Message\ResponseFactoryInterface;
33
- use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface;
34
- use Psr\Http\Message\StreamFactoryInterface;
35
- use Psr\Http\Message\StreamInterface;
36
- use Psr\Http\Message\UriFactoryInterface;
37
- use Psr\Http\Message\UriInterface;
38
- use Symfony\Component\HttpClient\Internal\HttplugWaitLoop;
39
- use Symfony\Component\HttpClient\Response\HttplugPromise;
40
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
41
- use Symfony\Contracts\HttpClient\HttpClientInterface;
42
- use Symfony\Contracts\HttpClient\ResponseInterface;
43
- use Symfony\Contracts\Service\ResetInterface;
44
-
45
- if (!interface_exists(HttplugInterface::class)) {
46
- throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/httplug" package is not installed. Try running "composer require php-http/httplug".');
47
- }
48
-
49
- if (!interface_exists(RequestFactory::class)) {
50
- throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/message-factory" package is not installed. Try running "composer require nyholm/psr7".');
51
- }
52
-
53
- /**
54
- * An adapter to turn a Symfony HttpClientInterface into an Httplug client.
55
- *
56
- * Run "composer require nyholm/psr7" to install an efficient implementation of response
57
- * and stream factories with flex-provided autowiring aliases.
58
- *
59
- * @author Nicolas Grekas <p@tchwork.com>
60
- */
61
- final class HttplugClient implements HttplugInterface, HttpAsyncClient, RequestFactory, StreamFactory, UriFactory, ResetInterface
62
- {
63
- private $client;
64
- private $responseFactory;
65
- private $streamFactory;
66
-
67
- /**
68
- * @var \SplObjectStorage<ResponseInterface, array{RequestInterface, Promise}>|null
69
- */
70
- private $promisePool;
71
-
72
- private $waitLoop;
73
-
74
- public function __construct(HttpClientInterface $client = null, ResponseFactoryInterface $responseFactory = null, StreamFactoryInterface $streamFactory = null)
75
- {
76
- $this->client = $client ?? HttpClient::create();
77
- $this->responseFactory = $responseFactory;
78
- $this->streamFactory = $streamFactory ?? ($responseFactory instanceof StreamFactoryInterface ? $responseFactory : null);
79
- $this->promisePool = class_exists(Utils::class) ? new \SplObjectStorage() : null;
80
-
81
- if (null === $this->responseFactory || null === $this->streamFactory) {
82
- if (!class_exists(Psr17Factory::class) && !class_exists(Psr17FactoryDiscovery::class)) {
83
- throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been provided. Try running "composer require nyholm/psr7".');
84
- }
85
-
86
- try {
87
- $psr17Factory = class_exists(Psr17Factory::class, false) ? new Psr17Factory() : null;
88
- $this->responseFactory = $this->responseFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findResponseFactory();
89
- $this->streamFactory = $this->streamFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findStreamFactory();
90
- } catch (NotFoundException $e) {
91
- throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been found. Try running "composer require nyholm/psr7".', 0, $e);
92
- }
93
- }
94
-
95
- $this->waitLoop = new HttplugWaitLoop($this->client, $this->promisePool, $this->responseFactory, $this->streamFactory);
96
- }
97
-
98
- /**
99
- * {@inheritdoc}
100
- */
101
- public function sendRequest(RequestInterface $request): Psr7ResponseInterface
102
- {
103
- try {
104
- return $this->waitLoop->createPsr7Response($this->sendPsr7Request($request));
105
- } catch (TransportExceptionInterface $e) {
106
- throw new NetworkException($e->getMessage(), $request, $e);
107
- }
108
- }
109
-
110
- /**
111
- * {@inheritdoc}
112
- *
113
- * @return HttplugPromise
114
- */
115
- public function sendAsyncRequest(RequestInterface $request): Promise
116
- {
117
- if (!$promisePool = $this->promisePool) {
118
- throw new \LogicException(sprintf('You cannot use "%s()" as the "guzzlehttp/promises" package is not installed. Try running "composer require guzzlehttp/promises".', __METHOD__));
119
- }
120
-
121
- try {
122
- $response = $this->sendPsr7Request($request, true);
123
- } catch (NetworkException $e) {
124
- return new HttplugPromise(new RejectedPromise($e));
125
- }
126
-
127
- $waitLoop = $this->waitLoop;
128
-
129
- $promise = new GuzzlePromise(static function () use ($response, $waitLoop) {
130
- $waitLoop->wait($response);
131
- }, static function () use ($response, $promisePool) {
132
- $response->cancel();
133
- unset($promisePool[$response]);
134
- });
135
-
136
- $promisePool[$response] = [$request, $promise];
137
-
138
- return new HttplugPromise($promise);
139
- }
140
-
141
- /**
142
- * Resolves pending promises that complete before the timeouts are reached.
143
- *
144
- * When $maxDuration is null and $idleTimeout is reached, promises are rejected.
145
- *
146
- * @return int The number of remaining pending promises
147
- */
148
- public function wait(float $maxDuration = null, float $idleTimeout = null): int
149
- {
150
- return $this->waitLoop->wait(null, $maxDuration, $idleTimeout);
151
- }
152
-
153
- /**
154
- * {@inheritdoc}
155
- */
156
- public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1'): RequestInterface
157
- {
158
- if ($this->responseFactory instanceof RequestFactoryInterface) {
159
- $request = $this->responseFactory->createRequest($method, $uri);
160
- } elseif (class_exists(Request::class)) {
161
- $request = new Request($method, $uri);
162
- } elseif (class_exists(Psr17FactoryDiscovery::class)) {
163
- $request = Psr17FactoryDiscovery::findRequestFactory()->createRequest($method, $uri);
164
- } else {
165
- throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
166
- }
167
-
168
- $request = $request
169
- ->withProtocolVersion($protocolVersion)
170
- ->withBody($this->createStream($body))
171
- ;
172
-
173
- foreach ($headers as $name => $value) {
174
- $request = $request->withAddedHeader($name, $value);
175
- }
176
-
177
- return $request;
178
- }
179
-
180
- /**
181
- * {@inheritdoc}
182
- */
183
- public function createStream($body = null): StreamInterface
184
- {
185
- if ($body instanceof StreamInterface) {
186
- return $body;
187
- }
188
-
189
- if (\is_string($body ?? '')) {
190
- $stream = $this->streamFactory->createStream($body ?? '');
191
- } elseif (\is_resource($body)) {
192
- $stream = $this->streamFactory->createStreamFromResource($body);
193
- } else {
194
- throw new \InvalidArgumentException(sprintf('"%s()" expects string, resource or StreamInterface, "%s" given.', __METHOD__, get_debug_type($body)));
195
- }
196
-
197
- if ($stream->isSeekable()) {
198
- $stream->seek(0);
199
- }
200
-
201
- return $stream;
202
- }
203
-
204
- /**
205
- * {@inheritdoc}
206
- */
207
- public function createUri($uri): UriInterface
208
- {
209
- if ($uri instanceof UriInterface) {
210
- return $uri;
211
- }
212
-
213
- if ($this->responseFactory instanceof UriFactoryInterface) {
214
- return $this->responseFactory->createUri($uri);
215
- }
216
-
217
- if (class_exists(Uri::class)) {
218
- return new Uri($uri);
219
- }
220
-
221
- if (class_exists(Psr17FactoryDiscovery::class)) {
222
- return Psr17FactoryDiscovery::findUrlFactory()->createUri($uri);
223
- }
224
-
225
- throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
226
- }
227
-
228
- public function __sleep(): array
229
- {
230
- throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
231
- }
232
-
233
- public function __wakeup()
234
- {
235
- throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
236
- }
237
-
238
- public function __destruct()
239
- {
240
- $this->wait();
241
- }
242
-
243
- public function reset()
244
- {
245
- if ($this->client instanceof ResetInterface) {
246
- $this->client->reset();
247
- }
248
- }
249
-
250
- private function sendPsr7Request(RequestInterface $request, bool $buffer = null): ResponseInterface
251
- {
252
- try {
253
- $body = $request->getBody();
254
-
255
- if ($body->isSeekable()) {
256
- $body->seek(0);
257
- }
258
-
259
- return $this->client->request($request->getMethod(), (string) $request->getUri(), [
260
- 'headers' => $request->getHeaders(),
261
- 'body' => $body->getContents(),
262
- 'http_version' => '1.0' === $request->getProtocolVersion() ? '1.0' : null,
263
- 'buffer' => $buffer,
264
- ]);
265
- } catch (\InvalidArgumentException $e) {
266
- throw new RequestException($e->getMessage(), $request, $e);
267
- } catch (TransportExceptionInterface $e) {
268
- throw new NetworkException($e->getMessage(), $request, $e);
269
- }
270
- }
271
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/AmpBody.php DELETED
@@ -1,142 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- use Amp\ByteStream\InputStream;
15
- use Amp\ByteStream\ResourceInputStream;
16
- use Amp\Http\Client\RequestBody;
17
- use Amp\Promise;
18
- use Amp\Success;
19
- use Symfony\Component\HttpClient\Exception\TransportException;
20
-
21
- /**
22
- * @author Nicolas Grekas <p@tchwork.com>
23
- *
24
- * @internal
25
- */
26
- class AmpBody implements RequestBody, InputStream
27
- {
28
- private $body;
29
- private $info;
30
- private $onProgress;
31
- private $offset = 0;
32
- private $length = -1;
33
- private $uploaded;
34
-
35
- public function __construct($body, &$info, \Closure $onProgress)
36
- {
37
- $this->body = $body;
38
- $this->info = &$info;
39
- $this->onProgress = $onProgress;
40
-
41
- if (\is_resource($body)) {
42
- $this->offset = ftell($body);
43
- $this->length = fstat($body)['size'];
44
- $this->body = new ResourceInputStream($body);
45
- } elseif (\is_string($body)) {
46
- $this->length = \strlen($body);
47
- }
48
- }
49
-
50
- public function createBodyStream(): InputStream
51
- {
52
- if (null !== $this->uploaded) {
53
- $this->uploaded = null;
54
-
55
- if (\is_string($this->body)) {
56
- $this->offset = 0;
57
- } elseif ($this->body instanceof ResourceInputStream) {
58
- fseek($this->body->getResource(), $this->offset);
59
- }
60
- }
61
-
62
- return $this;
63
- }
64
-
65
- public function getHeaders(): Promise
66
- {
67
- return new Success([]);
68
- }
69
-
70
- public function getBodyLength(): Promise
71
- {
72
- return new Success($this->length - $this->offset);
73
- }
74
-
75
- public function read(): Promise
76
- {
77
- $this->info['size_upload'] += $this->uploaded;
78
- $this->uploaded = 0;
79
- ($this->onProgress)();
80
-
81
- $chunk = $this->doRead();
82
- $chunk->onResolve(function ($e, $data) {
83
- if (null !== $data) {
84
- $this->uploaded = \strlen($data);
85
- } else {
86
- $this->info['upload_content_length'] = $this->info['size_upload'];
87
- }
88
- });
89
-
90
- return $chunk;
91
- }
92
-
93
- public static function rewind(RequestBody $body): RequestBody
94
- {
95
- if (!$body instanceof self) {
96
- return $body;
97
- }
98
-
99
- $body->uploaded = null;
100
-
101
- if ($body->body instanceof ResourceInputStream) {
102
- fseek($body->body->getResource(), $body->offset);
103
-
104
- return new $body($body->body, $body->info, $body->onProgress);
105
- }
106
-
107
- if (\is_string($body->body)) {
108
- $body->offset = 0;
109
- }
110
-
111
- return $body;
112
- }
113
-
114
- private function doRead(): Promise
115
- {
116
- if ($this->body instanceof ResourceInputStream) {
117
- return $this->body->read();
118
- }
119
-
120
- if (null === $this->offset || !$this->length) {
121
- return new Success();
122
- }
123
-
124
- if (\is_string($this->body)) {
125
- $this->offset = null;
126
-
127
- return new Success($this->body);
128
- }
129
-
130
- if ('' === $data = ($this->body)(16372)) {
131
- $this->offset = null;
132
-
133
- return new Success();
134
- }
135
-
136
- if (!\is_string($data)) {
137
- throw new TransportException(sprintf('Return value of the "body" option callback must be string, "%s" returned.', get_debug_type($data)));
138
- }
139
-
140
- return new Success($data);
141
- }
142
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/AmpClientState.php DELETED
@@ -1,217 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- use Amp\CancellationToken;
15
- use Amp\Deferred;
16
- use Amp\Http\Client\Connection\ConnectionLimitingPool;
17
- use Amp\Http\Client\Connection\DefaultConnectionFactory;
18
- use Amp\Http\Client\InterceptedHttpClient;
19
- use Amp\Http\Client\Interceptor\RetryRequests;
20
- use Amp\Http\Client\PooledHttpClient;
21
- use Amp\Http\Client\Request;
22
- use Amp\Http\Client\Response;
23
- use Amp\Http\Tunnel\Http1TunnelConnector;
24
- use Amp\Http\Tunnel\Https1TunnelConnector;
25
- use Amp\Promise;
26
- use Amp\Socket\Certificate;
27
- use Amp\Socket\ClientTlsContext;
28
- use Amp\Socket\ConnectContext;
29
- use Amp\Socket\Connector;
30
- use Amp\Socket\DnsConnector;
31
- use Amp\Socket\SocketAddress;
32
- use Amp\Success;
33
- use Psr\Log\LoggerInterface;
34
-
35
- /**
36
- * Internal representation of the Amp client's state.
37
- *
38
- * @author Nicolas Grekas <p@tchwork.com>
39
- *
40
- * @internal
41
- */
42
- final class AmpClientState extends ClientState
43
- {
44
- public $dnsCache = [];
45
- public $responseCount = 0;
46
- public $pushedResponses = [];
47
-
48
- private $clients = [];
49
- private $clientConfigurator;
50
- private $maxHostConnections;
51
- private $maxPendingPushes;
52
- private $logger;
53
-
54
- public function __construct(?callable $clientConfigurator, int $maxHostConnections, int $maxPendingPushes, ?LoggerInterface &$logger)
55
- {
56
- $this->clientConfigurator = $clientConfigurator ?? static function (PooledHttpClient $client) {
57
- return new InterceptedHttpClient($client, new RetryRequests(2));
58
- };
59
- $this->maxHostConnections = $maxHostConnections;
60
- $this->maxPendingPushes = $maxPendingPushes;
61
- $this->logger = &$logger;
62
- }
63
-
64
- /**
65
- * @return Promise<Response>
66
- */
67
- public function request(array $options, Request $request, CancellationToken $cancellation, array &$info, \Closure $onProgress, &$handle): Promise
68
- {
69
- if ($options['proxy']) {
70
- if ($request->hasHeader('proxy-authorization')) {
71
- $options['proxy']['auth'] = $request->getHeader('proxy-authorization');
72
- }
73
-
74
- // Matching "no_proxy" should follow the behavior of curl
75
- $host = $request->getUri()->getHost();
76
- foreach ($options['proxy']['no_proxy'] as $rule) {
77
- $dotRule = '.'.ltrim($rule, '.');
78
-
79
- if ('*' === $rule || $host === $rule || substr($host, -\strlen($dotRule)) === $dotRule) {
80
- $options['proxy'] = null;
81
- break;
82
- }
83
- }
84
- }
85
-
86
- $request = clone $request;
87
-
88
- if ($request->hasHeader('proxy-authorization')) {
89
- $request->removeHeader('proxy-authorization');
90
- }
91
-
92
- if ($options['capture_peer_cert_chain']) {
93
- $info['peer_certificate_chain'] = [];
94
- }
95
-
96
- $request->addEventListener(new AmpListener($info, $options['peer_fingerprint']['pin-sha256'] ?? [], $onProgress, $handle));
97
- $request->setPushHandler(function ($request, $response) use ($options): Promise {
98
- return $this->handlePush($request, $response, $options);
99
- });
100
-
101
- ($request->hasHeader('content-length') ? new Success((int) $request->getHeader('content-length')) : $request->getBody()->getBodyLength())
102
- ->onResolve(static function ($e, $bodySize) use (&$info) {
103
- if (null !== $bodySize && 0 <= $bodySize) {
104
- $info['upload_content_length'] = ((1 + $info['upload_content_length']) ?? 1) - 1 + $bodySize;
105
- }
106
- });
107
-
108
- [$client, $connector] = $this->getClient($options);
109
- $response = $client->request($request, $cancellation);
110
- $response->onResolve(static function ($e) use ($connector, &$handle) {
111
- if (null === $e) {
112
- $handle = $connector->handle;
113
- }
114
- });
115
-
116
- return $response;
117
- }
118
-
119
- private function getClient(array $options): array
120
- {
121
- $options = [
122
- 'bindto' => $options['bindto'] ?: '0',
123
- 'verify_peer' => $options['verify_peer'],
124
- 'capath' => $options['capath'],
125
- 'cafile' => $options['cafile'],
126
- 'local_cert' => $options['local_cert'],
127
- 'local_pk' => $options['local_pk'],
128
- 'ciphers' => $options['ciphers'],
129
- 'capture_peer_cert_chain' => $options['capture_peer_cert_chain'] || $options['peer_fingerprint'],
130
- 'proxy' => $options['proxy'],
131
- ];
132
-
133
- $key = md5(serialize($options));
134
-
135
- if (isset($this->clients[$key])) {
136
- return $this->clients[$key];
137
- }
138
-
139
- $context = new ClientTlsContext('');
140
- $options['verify_peer'] || $context = $context->withoutPeerVerification();
141
- $options['cafile'] && $context = $context->withCaFile($options['cafile']);
142
- $options['capath'] && $context = $context->withCaPath($options['capath']);
143
- $options['local_cert'] && $context = $context->withCertificate(new Certificate($options['local_cert'], $options['local_pk']));
144
- $options['ciphers'] && $context = $context->withCiphers($options['ciphers']);
145
- $options['capture_peer_cert_chain'] && $context = $context->withPeerCapturing();
146
-
147
- $connector = $handleConnector = new class() implements Connector {
148
- public $connector;
149
- public $uri;
150
- public $handle;
151
-
152
- public function connect(string $uri, ConnectContext $context = null, CancellationToken $token = null): Promise
153
- {
154
- $result = $this->connector->connect($this->uri ?? $uri, $context, $token);
155
- $result->onResolve(function ($e, $socket) {
156
- $this->handle = null !== $socket ? $socket->getResource() : false;
157
- });
158
-
159
- return $result;
160
- }
161
- };
162
- $connector->connector = new DnsConnector(new AmpResolver($this->dnsCache));
163
-
164
- $context = (new ConnectContext())
165
- ->withTcpNoDelay()
166
- ->withTlsContext($context);
167
-
168
- if ($options['bindto']) {
169
- if (file_exists($options['bindto'])) {
170
- $connector->uri = 'unix://'.$options['bindto'];
171
- } else {
172
- $context = $context->withBindTo($options['bindto']);
173
- }
174
- }
175
-
176
- if ($options['proxy']) {
177
- $proxyUrl = parse_url($options['proxy']['url']);
178
- $proxySocket = new SocketAddress($proxyUrl['host'], $proxyUrl['port']);
179
- $proxyHeaders = $options['proxy']['auth'] ? ['Proxy-Authorization' => $options['proxy']['auth']] : [];
180
-
181
- if ('ssl' === $proxyUrl['scheme']) {
182
- $connector = new Https1TunnelConnector($proxySocket, $context->getTlsContext(), $proxyHeaders, $connector);
183
- } else {
184
- $connector = new Http1TunnelConnector($proxySocket, $proxyHeaders, $connector);
185
- }
186
- }
187
-
188
- $maxHostConnections = 0 < $this->maxHostConnections ? $this->maxHostConnections : \PHP_INT_MAX;
189
- $pool = new DefaultConnectionFactory($connector, $context);
190
- $pool = ConnectionLimitingPool::byAuthority($maxHostConnections, $pool);
191
-
192
- return $this->clients[$key] = [($this->clientConfigurator)(new PooledHttpClient($pool)), $handleConnector];
193
- }
194
-
195
- private function handlePush(Request $request, Promise $response, array $options): Promise
196
- {
197
- $deferred = new Deferred();
198
- $authority = $request->getUri()->getAuthority();
199
-
200
- if ($this->maxPendingPushes <= \count($this->pushedResponses[$authority] ?? [])) {
201
- $fifoUrl = key($this->pushedResponses[$authority]);
202
- unset($this->pushedResponses[$authority][$fifoUrl]);
203
- $this->logger && $this->logger->debug(sprintf('Evicting oldest pushed response: "%s"', $fifoUrl));
204
- }
205
-
206
- $url = (string) $request->getUri();
207
- $this->logger && $this->logger->debug(sprintf('Queueing pushed response: "%s"', $url));
208
- $this->pushedResponses[$authority][] = [$url, $deferred, $request, $response, [
209
- 'proxy' => $options['proxy'],
210
- 'bindto' => $options['bindto'],
211
- 'local_cert' => $options['local_cert'],
212
- 'local_pk' => $options['local_pk'],
213
- ]];
214
-
215
- return $deferred->promise();
216
- }
217
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/AmpListener.php DELETED
@@ -1,183 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- use Amp\Http\Client\Connection\Stream;
15
- use Amp\Http\Client\EventListener;
16
- use Amp\Http\Client\Request;
17
- use Amp\Promise;
18
- use Amp\Success;
19
- use Symfony\Component\HttpClient\Exception\TransportException;
20
-
21
- /**
22
- * @author Nicolas Grekas <p@tchwork.com>
23
- *
24
- * @internal
25
- */
26
- class AmpListener implements EventListener
27
- {
28
- private $info;
29
- private $pinSha256;
30
- private $onProgress;
31
- private $handle;
32
-
33
- public function __construct(array &$info, array $pinSha256, \Closure $onProgress, &$handle)
34
- {
35
- $info += [
36
- 'connect_time' => 0.0,
37
- 'pretransfer_time' => 0.0,
38
- 'starttransfer_time' => 0.0,
39
- 'total_time' => 0.0,
40
- 'namelookup_time' => 0.0,
41
- 'primary_ip' => '',
42
- 'primary_port' => 0,
43
- ];
44
-
45
- $this->info = &$info;
46
- $this->pinSha256 = $pinSha256;
47
- $this->onProgress = $onProgress;
48
- $this->handle = &$handle;
49
- }
50
-
51
- public function startRequest(Request $request): Promise
52
- {
53
- $this->info['start_time'] = $this->info['start_time'] ?? microtime(true);
54
- ($this->onProgress)();
55
-
56
- return new Success();
57
- }
58
-
59
- public function startDnsResolution(Request $request): Promise
60
- {
61
- ($this->onProgress)();
62
-
63
- return new Success();
64
- }
65
-
66
- public function startConnectionCreation(Request $request): Promise
67
- {
68
- ($this->onProgress)();
69
-
70
- return new Success();
71
- }
72
-
73
- public function startTlsNegotiation(Request $request): Promise
74
- {
75
- ($this->onProgress)();
76
-
77
- return new Success();
78
- }
79
-
80
- public function startSendingRequest(Request $request, Stream $stream): Promise
81
- {
82
- $host = $stream->getRemoteAddress()->getHost();
83
-
84
- if (false !== strpos($host, ':')) {
85
- $host = '['.$host.']';
86
- }
87
-
88
- $this->info['primary_ip'] = $host;
89
- $this->info['primary_port'] = $stream->getRemoteAddress()->getPort();
90
- $this->info['pretransfer_time'] = microtime(true) - $this->info['start_time'];
91
- $this->info['debug'] .= sprintf("* Connected to %s (%s) port %d\n", $request->getUri()->getHost(), $host, $this->info['primary_port']);
92
-
93
- if ((isset($this->info['peer_certificate_chain']) || $this->pinSha256) && null !== $tlsInfo = $stream->getTlsInfo()) {
94
- foreach ($tlsInfo->getPeerCertificates() as $cert) {
95
- $this->info['peer_certificate_chain'][] = openssl_x509_read($cert->toPem());
96
- }
97
-
98
- if ($this->pinSha256) {
99
- $pin = openssl_pkey_get_public($this->info['peer_certificate_chain'][0]);
100
- $pin = openssl_pkey_get_details($pin)['key'];
101
- $pin = \array_slice(explode("\n", $pin), 1, -2);
102
- $pin = base64_decode(implode('', $pin));
103
- $pin = base64_encode(hash('sha256', $pin, true));
104
-
105
- if (!\in_array($pin, $this->pinSha256, true)) {
106
- throw new TransportException(sprintf('SSL public key does not match pinned public key for "%s".', $this->info['url']));
107
- }
108
- }
109
- }
110
- ($this->onProgress)();
111
-
112
- $uri = $request->getUri();
113
- $requestUri = $uri->getPath() ?: '/';
114
-
115
- if ('' !== $query = $uri->getQuery()) {
116
- $requestUri .= '?'.$query;
117
- }
118
-
119
- if ('CONNECT' === $method = $request->getMethod()) {
120
- $requestUri = $uri->getHost().': '.($uri->getPort() ?? ('https' === $uri->getScheme() ? 443 : 80));
121
- }
122
-
123
- $this->info['debug'] .= sprintf("> %s %s HTTP/%s \r\n", $method, $requestUri, $request->getProtocolVersions()[0]);
124
-
125
- foreach ($request->getRawHeaders() as [$name, $value]) {
126
- $this->info['debug'] .= $name.': '.$value."\r\n";
127
- }
128
- $this->info['debug'] .= "\r\n";
129
-
130
- return new Success();
131
- }
132
-
133
- public function completeSendingRequest(Request $request, Stream $stream): Promise
134
- {
135
- ($this->onProgress)();
136
-
137
- return new Success();
138
- }
139
-
140
- public function startReceivingResponse(Request $request, Stream $stream): Promise
141
- {
142
- $this->info['starttransfer_time'] = microtime(true) - $this->info['start_time'];
143
- ($this->onProgress)();
144
-
145
- return new Success();
146
- }
147
-
148
- public function completeReceivingResponse(Request $request, Stream $stream): Promise
149
- {
150
- $this->handle = null;
151
- ($this->onProgress)();
152
-
153
- return new Success();
154
- }
155
-
156
- public function completeDnsResolution(Request $request): Promise
157
- {
158
- $this->info['namelookup_time'] = microtime(true) - $this->info['start_time'];
159
- ($this->onProgress)();
160
-
161
- return new Success();
162
- }
163
-
164
- public function completeConnectionCreation(Request $request): Promise
165
- {
166
- $this->info['connect_time'] = microtime(true) - $this->info['start_time'];
167
- ($this->onProgress)();
168
-
169
- return new Success();
170
- }
171
-
172
- public function completeTlsNegotiation(Request $request): Promise
173
- {
174
- ($this->onProgress)();
175
-
176
- return new Success();
177
- }
178
-
179
- public function abort(Request $request, \Throwable $cause): Promise
180
- {
181
- return new Success();
182
- }
183
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/AmpResolver.php DELETED
@@ -1,52 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- use Amp\Dns;
15
- use Amp\Dns\Record;
16
- use Amp\Promise;
17
- use Amp\Success;
18
-
19
- /**
20
- * Handles local overrides for the DNS resolver.
21
- *
22
- * @author Nicolas Grekas <p@tchwork.com>
23
- *
24
- * @internal
25
- */
26
- class AmpResolver implements Dns\Resolver
27
- {
28
- private $dnsMap;
29
-
30
- public function __construct(array &$dnsMap)
31
- {
32
- $this->dnsMap = &$dnsMap;
33
- }
34
-
35
- public function resolve(string $name, int $typeRestriction = null): Promise
36
- {
37
- if (!isset($this->dnsMap[$name]) || !\in_array($typeRestriction, [Record::A, null], true)) {
38
- return Dns\resolver()->resolve($name, $typeRestriction);
39
- }
40
-
41
- return new Success([new Record($this->dnsMap[$name], Record::A, null)]);
42
- }
43
-
44
- public function query(string $name, int $type): Promise
45
- {
46
- if (!isset($this->dnsMap[$name]) || Record::A !== $type) {
47
- return Dns\resolver()->query($name, $type);
48
- }
49
-
50
- return new Success([new Record($this->dnsMap[$name], Record::A, null)]);
51
- }
52
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/Canary.php DELETED
@@ -1,40 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- /**
15
- * @author Nicolas Grekas <p@tchwork.com>
16
- *
17
- * @internal
18
- */
19
- final class Canary
20
- {
21
- private $canceller;
22
-
23
- public function __construct(\Closure $canceller)
24
- {
25
- $this->canceller = $canceller;
26
- }
27
-
28
- public function cancel()
29
- {
30
- if (($canceller = $this->canceller) instanceof \Closure) {
31
- $this->canceller = null;
32
- $canceller();
33
- }
34
- }
35
-
36
- public function __destruct()
37
- {
38
- $this->cancel();
39
- }
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/ClientState.php DELETED
@@ -1,26 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- /**
15
- * Internal representation of the client state.
16
- *
17
- * @author Alexander M. Turek <me@derrabus.de>
18
- *
19
- * @internal
20
- */
21
- class ClientState
22
- {
23
- public $handlesActivity = [];
24
- public $openHandles = [];
25
- public $lastTimeout;
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/CurlClientState.php DELETED
@@ -1,148 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- use Psr\Log\LoggerInterface;
15
- use Symfony\Component\HttpClient\Response\CurlResponse;
16
-
17
- /**
18
- * Internal representation of the cURL client's state.
19
- *
20
- * @author Alexander M. Turek <me@derrabus.de>
21
- *
22
- * @internal
23
- */
24
- final class CurlClientState extends ClientState
25
- {
26
- /** @var \CurlMultiHandle|resource|null */
27
- public $handle;
28
- /** @var \CurlShareHandle|resource|null */
29
- public $share;
30
- /** @var PushedResponse[] */
31
- public $pushedResponses = [];
32
- /** @var DnsCache */
33
- public $dnsCache;
34
- /** @var float[] */
35
- public $pauseExpiries = [];
36
- public $execCounter = \PHP_INT_MIN;
37
- /** @var LoggerInterface|null */
38
- public $logger;
39
-
40
- public static $curlVersion;
41
-
42
- public function __construct(int $maxHostConnections, int $maxPendingPushes)
43
- {
44
- self::$curlVersion = self::$curlVersion ?? curl_version();
45
-
46
- $this->handle = curl_multi_init();
47
- $this->dnsCache = new DnsCache();
48
- $this->reset();
49
-
50
- // Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order
51
- if (\defined('CURLPIPE_MULTIPLEX')) {
52
- curl_multi_setopt($this->handle, \CURLMOPT_PIPELINING, \CURLPIPE_MULTIPLEX);
53
- }
54
- if (\defined('CURLMOPT_MAX_HOST_CONNECTIONS')) {
55
- $maxHostConnections = curl_multi_setopt($this->handle, \CURLMOPT_MAX_HOST_CONNECTIONS, 0 < $maxHostConnections ? $maxHostConnections : \PHP_INT_MAX) ? 0 : $maxHostConnections;
56
- }
57
- if (\defined('CURLMOPT_MAXCONNECTS') && 0 < $maxHostConnections) {
58
- curl_multi_setopt($this->handle, \CURLMOPT_MAXCONNECTS, $maxHostConnections);
59
- }
60
-
61
- // Skip configuring HTTP/2 push when it's unsupported or buggy, see https://bugs.php.net/77535
62
- if (0 >= $maxPendingPushes || \PHP_VERSION_ID < 70217 || (\PHP_VERSION_ID >= 70300 && \PHP_VERSION_ID < 70304)) {
63
- return;
64
- }
65
-
66
- // HTTP/2 push crashes before curl 7.61
67
- if (!\defined('CURLMOPT_PUSHFUNCTION') || 0x073D00 > self::$curlVersion['version_number'] || !(\CURL_VERSION_HTTP2 & self::$curlVersion['features'])) {
68
- return;
69
- }
70
-
71
- // Clone to prevent a circular reference
72
- $multi = clone $this;
73
- $multi->handle = null;
74
- $multi->share = null;
75
- $multi->pushedResponses = &$this->pushedResponses;
76
- $multi->logger = &$this->logger;
77
- $multi->handlesActivity = &$this->handlesActivity;
78
- $multi->openHandles = &$this->openHandles;
79
-
80
- curl_multi_setopt($this->handle, \CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi, $maxPendingPushes) {
81
- return $multi->handlePush($parent, $pushed, $requestHeaders, $maxPendingPushes);
82
- });
83
- }
84
-
85
- public function reset()
86
- {
87
- foreach ($this->pushedResponses as $url => $response) {
88
- $this->logger && $this->logger->debug(sprintf('Unused pushed response: "%s"', $url));
89
- curl_multi_remove_handle($this->handle, $response->handle);
90
- curl_close($response->handle);
91
- }
92
-
93
- $this->pushedResponses = [];
94
- $this->dnsCache->evictions = $this->dnsCache->evictions ?: $this->dnsCache->removals;
95
- $this->dnsCache->removals = $this->dnsCache->hostnames = [];
96
-
97
- $this->share = curl_share_init();
98
-
99
- curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_DNS);
100
- curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_SSL_SESSION);
101
-
102
- if (\defined('CURL_LOCK_DATA_CONNECT')) {
103
- curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_CONNECT);
104
- }
105
- }
106
-
107
- private function handlePush($parent, $pushed, array $requestHeaders, int $maxPendingPushes): int
108
- {
109
- $headers = [];
110
- $origin = curl_getinfo($parent, \CURLINFO_EFFECTIVE_URL);
111
-
112
- foreach ($requestHeaders as $h) {
113
- if (false !== $i = strpos($h, ':', 1)) {
114
- $headers[substr($h, 0, $i)][] = substr($h, 1 + $i);
115
- }
116
- }
117
-
118
- if (!isset($headers[':method']) || !isset($headers[':scheme']) || !isset($headers[':authority']) || !isset($headers[':path'])) {
119
- $this->logger && $this->logger->debug(sprintf('Rejecting pushed response from "%s": pushed headers are invalid', $origin));
120
-
121
- return \CURL_PUSH_DENY;
122
- }
123
-
124
- $url = $headers[':scheme'][0].'://'.$headers[':authority'][0];
125
-
126
- // curl before 7.65 doesn't validate the pushed ":authority" header,
127
- // but this is a MUST in the HTTP/2 RFC; let's restrict pushes to the original host,
128
- // ignoring domains mentioned as alt-name in the certificate for now (same as curl).
129
- if (!str_starts_with($origin, $url.'/')) {
130
- $this->logger && $this->logger->debug(sprintf('Rejecting pushed response from "%s": server is not authoritative for "%s"', $origin, $url));
131
-
132
- return \CURL_PUSH_DENY;
133
- }
134
-
135
- if ($maxPendingPushes <= \count($this->pushedResponses)) {
136
- $fifoUrl = key($this->pushedResponses);
137
- unset($this->pushedResponses[$fifoUrl]);
138
- $this->logger && $this->logger->debug(sprintf('Evicting oldest pushed response: "%s"', $fifoUrl));
139
- }
140
-
141
- $url .= $headers[':path'][0];
142
- $this->logger && $this->logger->debug(sprintf('Queueing pushed response: "%s"', $url));
143
-
144
- $this->pushedResponses[$url] = new PushedResponse(new CurlResponse($this, $pushed), $headers, $this->openHandles[(int) $parent][1] ?? [], $pushed);
145
-
146
- return \CURL_PUSH_OK;
147
- }
148
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/DnsCache.php DELETED
@@ -1,39 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- /**
15
- * Cache for resolved DNS queries.
16
- *
17
- * @author Alexander M. Turek <me@derrabus.de>
18
- *
19
- * @internal
20
- */
21
- final class DnsCache
22
- {
23
- /**
24
- * Resolved hostnames (hostname => IP address).
25
- *
26
- * @var string[]
27
- */
28
- public $hostnames = [];
29
-
30
- /**
31
- * @var string[]
32
- */
33
- public $removals = [];
34
-
35
- /**
36
- * @var string[]
37
- */
38
- public $evictions = [];
39
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/HttplugWaitLoop.php DELETED
@@ -1,141 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- use Http\Client\Exception\NetworkException;
15
- use Http\Promise\Promise;
16
- use Psr\Http\Message\RequestInterface as Psr7RequestInterface;
17
- use Psr\Http\Message\ResponseFactoryInterface;
18
- use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface;
19
- use Psr\Http\Message\StreamFactoryInterface;
20
- use Symfony\Component\HttpClient\Response\StreamableInterface;
21
- use Symfony\Component\HttpClient\Response\StreamWrapper;
22
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
23
- use Symfony\Contracts\HttpClient\HttpClientInterface;
24
- use Symfony\Contracts\HttpClient\ResponseInterface;
25
-
26
- /**
27
- * @author Nicolas Grekas <p@tchwork.com>
28
- *
29
- * @internal
30
- */
31
- final class HttplugWaitLoop
32
- {
33
- private $client;
34
- private $promisePool;
35
- private $responseFactory;
36
- private $streamFactory;
37
-
38
- /**
39
- * @param \SplObjectStorage<ResponseInterface, array{Psr7RequestInterface, Promise}>|null $promisePool
40
- */
41
- public function __construct(HttpClientInterface $client, ?\SplObjectStorage $promisePool, ResponseFactoryInterface $responseFactory, StreamFactoryInterface $streamFactory)
42
- {
43
- $this->client = $client;
44
- $this->promisePool = $promisePool;
45
- $this->responseFactory = $responseFactory;
46
- $this->streamFactory = $streamFactory;
47
- }
48
-
49
- public function wait(?ResponseInterface $pendingResponse, float $maxDuration = null, float $idleTimeout = null): int
50
- {
51
- if (!$this->promisePool) {
52
- return 0;
53
- }
54
-
55
- $guzzleQueue = \GuzzleHttp\Promise\Utils::queue();
56
-
57
- if (0.0 === $remainingDuration = $maxDuration) {
58
- $idleTimeout = 0.0;
59
- } elseif (null !== $maxDuration) {
60
- $startTime = microtime(true);
61
- $idleTimeout = max(0.0, min($maxDuration / 5, $idleTimeout ?? $maxDuration));
62
- }
63
-
64
- do {
65
- foreach ($this->client->stream($this->promisePool, $idleTimeout) as $response => $chunk) {
66
- try {
67
- if (null !== $maxDuration && $chunk->isTimeout()) {
68
- goto check_duration;
69
- }
70
-
71
- if ($chunk->isFirst()) {
72
- // Deactivate throwing on 3/4/5xx
73
- $response->getStatusCode();
74
- }
75
-
76
- if (!$chunk->isLast()) {
77
- goto check_duration;
78
- }
79
-
80
- if ([, $promise] = $this->promisePool[$response] ?? null) {
81
- unset($this->promisePool[$response]);
82
- $promise->resolve($this->createPsr7Response($response, true));
83
- }
84
- } catch (\Exception $e) {
85
- if ([$request, $promise] = $this->promisePool[$response] ?? null) {
86
- unset($this->promisePool[$response]);
87
-
88
- if ($e instanceof TransportExceptionInterface) {
89
- $e = new NetworkException($e->getMessage(), $request, $e);
90
- }
91
-
92
- $promise->reject($e);
93
- }
94
- }
95
-
96
- $guzzleQueue->run();
97
-
98
- if ($pendingResponse === $response) {
99
- return $this->promisePool->count();
100
- }
101
-
102
- check_duration:
103
- if (null !== $maxDuration && $idleTimeout && $idleTimeout > $remainingDuration = max(0.0, $maxDuration - microtime(true) + $startTime)) {
104
- $idleTimeout = $remainingDuration / 5;
105
- break;
106
- }
107
- }
108
-
109
- if (!$count = $this->promisePool->count()) {
110
- return 0;
111
- }
112
- } while (null === $maxDuration || 0 < $remainingDuration);
113
-
114
- return $count;
115
- }
116
-
117
- public function createPsr7Response(ResponseInterface $response, bool $buffer = false): Psr7ResponseInterface
118
- {
119
- $psrResponse = $this->responseFactory->createResponse($response->getStatusCode());
120
-
121
- foreach ($response->getHeaders(false) as $name => $values) {
122
- foreach ($values as $value) {
123
- $psrResponse = $psrResponse->withAddedHeader($name, $value);
124
- }
125
- }
126
-
127
- if ($response instanceof StreamableInterface) {
128
- $body = $this->streamFactory->createStreamFromResource($response->toStream(false));
129
- } elseif (!$buffer) {
130
- $body = $this->streamFactory->createStreamFromResource(StreamWrapper::createResource($response, $this->client));
131
- } else {
132
- $body = $this->streamFactory->createStream($response->getContent(false));
133
- }
134
-
135
- if ($body->isSeekable()) {
136
- $body->seek(0);
137
- }
138
-
139
- return $psrResponse->withBody($body);
140
- }
141
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/NativeClientState.php DELETED
@@ -1,47 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- /**
15
- * Internal representation of the native client's state.
16
- *
17
- * @author Alexander M. Turek <me@derrabus.de>
18
- *
19
- * @internal
20
- */
21
- final class NativeClientState extends ClientState
22
- {
23
- /** @var int */
24
- public $id;
25
- /** @var int */
26
- public $maxHostConnections = \PHP_INT_MAX;
27
- /** @var int */
28
- public $responseCount = 0;
29
- /** @var string[] */
30
- public $dnsCache = [];
31
- /** @var bool */
32
- public $sleep = false;
33
- /** @var int[] */
34
- public $hosts = [];
35
-
36
- public function __construct()
37
- {
38
- $this->id = random_int(\PHP_INT_MIN, \PHP_INT_MAX);
39
- }
40
-
41
- public function reset()
42
- {
43
- $this->responseCount = 0;
44
- $this->dnsCache = [];
45
- $this->hosts = [];
46
- }
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Internal/PushedResponse.php DELETED
@@ -1,41 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Internal;
13
-
14
- use Symfony\Component\HttpClient\Response\CurlResponse;
15
-
16
- /**
17
- * A pushed response with its request headers.
18
- *
19
- * @author Alexander M. Turek <me@derrabus.de>
20
- *
21
- * @internal
22
- */
23
- final class PushedResponse
24
- {
25
- public $response;
26
-
27
- /** @var string[] */
28
- public $requestHeaders;
29
-
30
- public $parentOptions = [];
31
-
32
- public $handle;
33
-
34
- public function __construct(CurlResponse $response, array $requestHeaders, array $parentOptions, $handle)
35
- {
36
- $this->response = $response;
37
- $this->requestHeaders = $requestHeaders;
38
- $this->parentOptions = $parentOptions;
39
- $this->handle = $handle;
40
- }
41
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/LICENSE DELETED
@@ -1,19 +0,0 @@
1
- Copyright (c) 2018-2022 Fabien Potencier
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/symfony/http-client/MockHttpClient.php DELETED
@@ -1,124 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Symfony\Component\HttpClient\Exception\TransportException;
15
- use Symfony\Component\HttpClient\Response\MockResponse;
16
- use Symfony\Component\HttpClient\Response\ResponseStream;
17
- use Symfony\Contracts\HttpClient\HttpClientInterface;
18
- use Symfony\Contracts\HttpClient\ResponseInterface;
19
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
20
- use Symfony\Contracts\Service\ResetInterface;
21
-
22
- /**
23
- * A test-friendly HttpClient that doesn't make actual HTTP requests.
24
- *
25
- * @author Nicolas Grekas <p@tchwork.com>
26
- */
27
- class MockHttpClient implements HttpClientInterface, ResetInterface
28
- {
29
- use HttpClientTrait;
30
-
31
- private $responseFactory;
32
- private $requestsCount = 0;
33
- private $defaultOptions = [];
34
-
35
- /**
36
- * @param callable|callable[]|ResponseInterface|ResponseInterface[]|iterable|null $responseFactory
37
- */
38
- public function __construct($responseFactory = null, ?string $baseUri = 'https://example.com')
39
- {
40
- $this->setResponseFactory($responseFactory);
41
- $this->defaultOptions['base_uri'] = $baseUri;
42
- }
43
-
44
- /**
45
- * @param callable|callable[]|ResponseInterface|ResponseInterface[]|iterable|null $responseFactory
46
- */
47
- public function setResponseFactory($responseFactory): void
48
- {
49
- if ($responseFactory instanceof ResponseInterface) {
50
- $responseFactory = [$responseFactory];
51
- }
52
-
53
- if (!$responseFactory instanceof \Iterator && null !== $responseFactory && !\is_callable($responseFactory)) {
54
- $responseFactory = (static function () use ($responseFactory) {
55
- yield from $responseFactory;
56
- })();
57
- }
58
-
59
- $this->responseFactory = $responseFactory;
60
- }
61
-
62
- /**
63
- * {@inheritdoc}
64
- */
65
- public function request(string $method, string $url, array $options = []): ResponseInterface
66
- {
67
- [$url, $options] = $this->prepareRequest($method, $url, $options, $this->defaultOptions, true);
68
- $url = implode('', $url);
69
-
70
- if (null === $this->responseFactory) {
71
- $response = new MockResponse();
72
- } elseif (\is_callable($this->responseFactory)) {
73
- $response = ($this->responseFactory)($method, $url, $options);
74
- } elseif (!$this->responseFactory->valid()) {
75
- throw new TransportException('The response factory iterator passed to MockHttpClient is empty.');
76
- } else {
77
- $responseFactory = $this->responseFactory->current();
78
- $response = \is_callable($responseFactory) ? $responseFactory($method, $url, $options) : $responseFactory;
79
- $this->responseFactory->next();
80
- }
81
- ++$this->requestsCount;
82
-
83
- if (!$response instanceof ResponseInterface) {
84
- throw new TransportException(sprintf('The response factory passed to MockHttpClient must return/yield an instance of ResponseInterface, "%s" given.', \is_object($response) ? \get_class($response) : \gettype($response)));
85
- }
86
-
87
- return MockResponse::fromRequest($method, $url, $options, $response);
88
- }
89
-
90
- /**
91
- * {@inheritdoc}
92
- */
93
- public function stream($responses, float $timeout = null): ResponseStreamInterface
94
- {
95
- if ($responses instanceof ResponseInterface) {
96
- $responses = [$responses];
97
- } elseif (!is_iterable($responses)) {
98
- throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of MockResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
99
- }
100
-
101
- return new ResponseStream(MockResponse::stream($responses, $timeout));
102
- }
103
-
104
- public function getRequestsCount(): int
105
- {
106
- return $this->requestsCount;
107
- }
108
-
109
- /**
110
- * {@inheritdoc}
111
- */
112
- public function withOptions(array $options): self
113
- {
114
- $clone = clone $this;
115
- $clone->defaultOptions = self::mergeDefaultOptions($options, $this->defaultOptions, true);
116
-
117
- return $clone;
118
- }
119
-
120
- public function reset()
121
- {
122
- $this->requestsCount = 0;
123
- }
124
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/NativeHttpClient.php DELETED
@@ -1,468 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Psr\Log\LoggerAwareInterface;
15
- use Psr\Log\LoggerAwareTrait;
16
- use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
17
- use Symfony\Component\HttpClient\Exception\TransportException;
18
- use Symfony\Component\HttpClient\Internal\NativeClientState;
19
- use Symfony\Component\HttpClient\Response\NativeResponse;
20
- use Symfony\Component\HttpClient\Response\ResponseStream;
21
- use Symfony\Contracts\HttpClient\HttpClientInterface;
22
- use Symfony\Contracts\HttpClient\ResponseInterface;
23
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
24
- use Symfony\Contracts\Service\ResetInterface;
25
-
26
- /**
27
- * A portable implementation of the HttpClientInterface contracts based on PHP stream wrappers.
28
- *
29
- * PHP stream wrappers are able to fetch response bodies concurrently,
30
- * but each request is opened synchronously.
31
- *
32
- * @author Nicolas Grekas <p@tchwork.com>
33
- */
34
- final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface
35
- {
36
- use HttpClientTrait;
37
- use LoggerAwareTrait;
38
-
39
- private $defaultOptions = self::OPTIONS_DEFAULTS;
40
- private static $emptyDefaults = self::OPTIONS_DEFAULTS;
41
-
42
- /** @var NativeClientState */
43
- private $multi;
44
-
45
- /**
46
- * @param array $defaultOptions Default request's options
47
- * @param int $maxHostConnections The maximum number of connections to open
48
- *
49
- * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
50
- */
51
- public function __construct(array $defaultOptions = [], int $maxHostConnections = 6)
52
- {
53
- $this->defaultOptions['buffer'] = $this->defaultOptions['buffer'] ?? \Closure::fromCallable([__CLASS__, 'shouldBuffer']);
54
-
55
- if ($defaultOptions) {
56
- [, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions);
57
- }
58
-
59
- $this->multi = new NativeClientState();
60
- $this->multi->maxHostConnections = 0 < $maxHostConnections ? $maxHostConnections : \PHP_INT_MAX;
61
- }
62
-
63
- /**
64
- * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
65
- *
66
- * {@inheritdoc}
67
- */
68
- public function request(string $method, string $url, array $options = []): ResponseInterface
69
- {
70
- [$url, $options] = self::prepareRequest($method, $url, $options, $this->defaultOptions);
71
-
72
- if ($options['bindto']) {
73
- if (file_exists($options['bindto'])) {
74
- throw new TransportException(__CLASS__.' cannot bind to local Unix sockets, use e.g. CurlHttpClient instead.');
75
- }
76
- if (str_starts_with($options['bindto'], 'if!')) {
77
- throw new TransportException(__CLASS__.' cannot bind to network interfaces, use e.g. CurlHttpClient instead.');
78
- }
79
- if (str_starts_with($options['bindto'], 'host!')) {
80
- $options['bindto'] = substr($options['bindto'], 5);
81
- }
82
- }
83
-
84
- $hasContentLength = isset($options['normalized_headers']['content-length']);
85
- $hasBody = '' !== $options['body'] || 'POST' === $method || $hasContentLength;
86
-
87
- $options['body'] = self::getBodyAsString($options['body']);
88
-
89
- if ('chunked' === substr($options['normalized_headers']['transfer-encoding'][0] ?? '', \strlen('Transfer-Encoding: '))) {
90
- unset($options['normalized_headers']['transfer-encoding']);
91
- $options['headers'] = array_merge(...array_values($options['normalized_headers']));
92
- $options['body'] = self::dechunk($options['body']);
93
- }
94
- if ('' === $options['body'] && $hasBody && !$hasContentLength) {
95
- $options['headers'][] = 'Content-Length: 0';
96
- }
97
- if ($hasBody && !isset($options['normalized_headers']['content-type'])) {
98
- $options['headers'][] = 'Content-Type: application/x-www-form-urlencoded';
99
- }
100
-
101
- if (\extension_loaded('zlib') && !isset($options['normalized_headers']['accept-encoding'])) {
102
- // gzip is the most widely available algo, no need to deal with deflate
103
- $options['headers'][] = 'Accept-Encoding: gzip';
104
- }
105
-
106
- if ($options['peer_fingerprint']) {
107
- if (isset($options['peer_fingerprint']['pin-sha256']) && 1 === \count($options['peer_fingerprint'])) {
108
- throw new TransportException(__CLASS__.' cannot verify "pin-sha256" fingerprints, please provide a "sha256" one.');
109
- }
110
-
111
- unset($options['peer_fingerprint']['pin-sha256']);
112
- }
113
-
114
- $info = [
115
- 'response_headers' => [],
116
- 'url' => $url,
117
- 'error' => null,
118
- 'canceled' => false,
119
- 'http_method' => $method,
120
- 'http_code' => 0,
121
- 'redirect_count' => 0,
122
- 'start_time' => 0.0,
123
- 'connect_time' => 0.0,
124
- 'redirect_time' => 0.0,
125
- 'pretransfer_time' => 0.0,
126
- 'starttransfer_time' => 0.0,
127
- 'total_time' => 0.0,
128
- 'namelookup_time' => 0.0,
129
- 'size_upload' => 0,
130
- 'size_download' => 0,
131
- 'size_body' => \strlen($options['body']),
132
- 'primary_ip' => '',
133
- 'primary_port' => 'http:' === $url['scheme'] ? 80 : 443,
134
- 'debug' => \extension_loaded('curl') ? '' : "* Enable the curl extension for better performance\n",
135
- ];
136
-
137
- if ($onProgress = $options['on_progress']) {
138
- // Memoize the last progress to ease calling the callback periodically when no network transfer happens
139
- $lastProgress = [0, 0];
140
- $maxDuration = 0 < $options['max_duration'] ? $options['max_duration'] : \INF;
141
- $onProgress = static function (...$progress) use ($onProgress, &$lastProgress, &$info, $maxDuration) {
142
- if ($info['total_time'] >= $maxDuration) {
143
- throw new TransportException(sprintf('Max duration was reached for "%s".', implode('', $info['url'])));
144
- }
145
-
146
- $progressInfo = $info;
147
- $progressInfo['url'] = implode('', $info['url']);
148
- unset($progressInfo['size_body']);
149
-
150
- if ($progress && -1 === $progress[0]) {
151
- // Response completed
152
- $lastProgress[0] = max($lastProgress);
153
- } else {
154
- $lastProgress = $progress ?: $lastProgress;
155
- }
156
-
157
- $onProgress($lastProgress[0], $lastProgress[1], $progressInfo);
158
- };
159
- } elseif (0 < $options['max_duration']) {
160
- $maxDuration = $options['max_duration'];
161
- $onProgress = static function () use (&$info, $maxDuration): void {
162
- if ($info['total_time'] >= $maxDuration) {
163
- throw new TransportException(sprintf('Max duration was reached for "%s".', implode('', $info['url'])));
164
- }
165
- };
166
- }
167
-
168
- // Always register a notification callback to compute live stats about the response
169
- $notification = static function (int $code, int $severity, ?string $msg, int $msgCode, int $dlNow, int $dlSize) use ($onProgress, &$info) {
170
- $info['total_time'] = microtime(true) - $info['start_time'];
171
-
172
- if (\STREAM_NOTIFY_PROGRESS === $code) {
173
- $info['starttransfer_time'] = $info['starttransfer_time'] ?: $info['total_time'];
174
- $info['size_upload'] += $dlNow ? 0 : $info['size_body'];
175
- $info['size_download'] = $dlNow;
176
- } elseif (\STREAM_NOTIFY_CONNECT === $code) {
177
- $info['connect_time'] = $info['total_time'];
178
- $info['debug'] .= $info['request_header'];
179
- unset($info['request_header']);
180
- } else {
181
- return;
182
- }
183
-
184
- if ($onProgress) {
185
- $onProgress($dlNow, $dlSize);
186
- }
187
- };
188
-
189
- if ($options['resolve']) {
190
- $this->multi->dnsCache = $options['resolve'] + $this->multi->dnsCache;
191
- }
192
-
193
- $this->logger && $this->logger->info(sprintf('Request: "%s %s"', $method, implode('', $url)));
194
-
195
- if (!isset($options['normalized_headers']['user-agent'])) {
196
- $options['headers'][] = 'User-Agent: Symfony HttpClient/Native';
197
- }
198
-
199
- if (0 < $options['max_duration']) {
200
- $options['timeout'] = min($options['max_duration'], $options['timeout']);
201
- }
202
-
203
- $bindto = $options['bindto'];
204
- if (!$bindto && (70322 === \PHP_VERSION_ID || 70410 === \PHP_VERSION_ID)) {
205
- $bindto = '0:0';
206
- }
207
-
208
- $context = [
209
- 'http' => [
210
- 'protocol_version' => min($options['http_version'] ?: '1.1', '1.1'),
211
- 'method' => $method,
212
- 'content' => $options['body'],
213
- 'ignore_errors' => true,
214
- 'curl_verify_ssl_peer' => $options['verify_peer'],
215
- 'curl_verify_ssl_host' => $options['verify_host'],
216
- 'auto_decode' => false, // Disable dechunk filter, it's incompatible with stream_select()
217
- 'timeout' => $options['timeout'],
218
- 'follow_location' => false, // We follow redirects ourselves - the native logic is too limited
219
- ],
220
- 'ssl' => array_filter([
221
- 'verify_peer' => $options['verify_peer'],
222
- 'verify_peer_name' => $options['verify_host'],
223
- 'cafile' => $options['cafile'],
224
- 'capath' => $options['capath'],
225
- 'local_cert' => $options['local_cert'],
226
- 'local_pk' => $options['local_pk'],
227
- 'passphrase' => $options['passphrase'],
228
- 'ciphers' => $options['ciphers'],
229
- 'peer_fingerprint' => $options['peer_fingerprint'],
230
- 'capture_peer_cert_chain' => $options['capture_peer_cert_chain'],
231
- 'allow_self_signed' => (bool) $options['peer_fingerprint'],
232
- 'SNI_enabled' => true,
233
- 'disable_compression' => true,
234
- ], static function ($v) { return null !== $v; }),
235
- 'socket' => [
236
- 'bindto' => $bindto,
237
- 'tcp_nodelay' => true,
238
- ],
239
- ];
240
-
241
- $context = stream_context_create($context, ['notification' => $notification]);
242
-
243
- $resolver = static function ($multi) use ($context, $options, $url, &$info, $onProgress) {
244
- [$host, $port] = self::parseHostPort($url, $info);
245
-
246
- if (!isset($options['normalized_headers']['host'])) {
247
- $options['headers'][] = 'Host: '.$host.$port;
248
- }
249
-
250
- $proxy = self::getProxy($options['proxy'], $url, $options['no_proxy']);
251
-
252
- if (!self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy, 'https:' === $url['scheme'])) {
253
- $ip = self::dnsResolve($host, $multi, $info, $onProgress);
254
- $url['authority'] = substr_replace($url['authority'], $ip, -\strlen($host) - \strlen($port), \strlen($host));
255
- }
256
-
257
- return [self::createRedirectResolver($options, $host, $proxy, $info, $onProgress), implode('', $url)];
258
- };
259
-
260
- return new NativeResponse($this->multi, $context, implode('', $url), $options, $info, $resolver, $onProgress, $this->logger);
261
- }
262
-
263
- /**
264
- * {@inheritdoc}
265
- */
266
- public function stream($responses, float $timeout = null): ResponseStreamInterface
267
- {
268
- if ($responses instanceof NativeResponse) {
269
- $responses = [$responses];
270
- } elseif (!is_iterable($responses)) {
271
- throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of NativeResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
272
- }
273
-
274
- return new ResponseStream(NativeResponse::stream($responses, $timeout));
275
- }
276
-
277
- public function reset()
278
- {
279
- $this->multi->reset();
280
- }
281
-
282
- private static function getBodyAsString($body): string
283
- {
284
- if (\is_resource($body)) {
285
- return stream_get_contents($body);
286
- }
287
-
288
- if (!$body instanceof \Closure) {
289
- return $body;
290
- }
291
-
292
- $result = '';
293
-
294
- while ('' !== $data = $body(self::$CHUNK_SIZE)) {
295
- if (!\is_string($data)) {
296
- throw new TransportException(sprintf('Return value of the "body" option callback must be string, "%s" returned.', get_debug_type($data)));
297
- }
298
-
299
- $result .= $data;
300
- }
301
-
302
- return $result;
303
- }
304
-
305
- /**
306
- * Extracts the host and the port from the URL.
307
- */
308
- private static function parseHostPort(array $url, array &$info): array
309
- {
310
- if ($port = parse_url($url['authority'], \PHP_URL_PORT) ?: '') {
311
- $info['primary_port'] = $port;
312
- $port = ':'.$port;
313
- } else {
314
- $info['primary_port'] = 'http:' === $url['scheme'] ? 80 : 443;
315
- }
316
-
317
- return [parse_url($url['authority'], \PHP_URL_HOST), $port];
318
- }
319
-
320
- /**
321
- * Resolves the IP of the host using the local DNS cache if possible.
322
- */
323
- private static function dnsResolve($host, NativeClientState $multi, array &$info, ?\Closure $onProgress): string
324
- {
325
- if (null === $ip = $multi->dnsCache[$host] ?? null) {
326
- $info['debug'] .= "* Hostname was NOT found in DNS cache\n";
327
- $now = microtime(true);
328
-
329
- if (!$ip = gethostbynamel($host)) {
330
- throw new TransportException(sprintf('Could not resolve host "%s".', $host));
331
- }
332
-
333
- $info['namelookup_time'] = microtime(true) - ($info['start_time'] ?: $now);
334
- $multi->dnsCache[$host] = $ip = $ip[0];
335
- $info['debug'] .= "* Added {$host}:0:{$ip} to DNS cache\n";
336
- } else {
337
- $info['debug'] .= "* Hostname was found in DNS cache\n";
338
- }
339
-
340
- $info['primary_ip'] = $ip;
341
-
342
- if ($onProgress) {
343
- // Notify DNS resolution
344
- $onProgress();
345
- }
346
-
347
- return $ip;
348
- }
349
-
350
- /**
351
- * Handles redirects - the native logic is too buggy to be used.
352
- */
353
- private static function createRedirectResolver(array $options, string $host, ?array $proxy, array &$info, ?\Closure $onProgress): \Closure
354
- {
355
- $redirectHeaders = [];
356
- if (0 < $maxRedirects = $options['max_redirects']) {
357
- $redirectHeaders = ['host' => $host];
358
- $redirectHeaders['with_auth'] = $redirectHeaders['no_auth'] = array_filter($options['headers'], static function ($h) {
359
- return 0 !== stripos($h, 'Host:');
360
- });
361
-
362
- if (isset($options['normalized_headers']['authorization']) || isset($options['normalized_headers']['cookie'])) {
363
- $redirectHeaders['no_auth'] = array_filter($redirectHeaders['no_auth'], static function ($h) {
364
- return 0 !== stripos($h, 'Authorization:') && 0 !== stripos($h, 'Cookie:');
365
- });
366
- }
367
- }
368
-
369
- return static function (NativeClientState $multi, ?string $location, $context) use (&$redirectHeaders, $proxy, &$info, $maxRedirects, $onProgress): ?string {
370
- if (null === $location || $info['http_code'] < 300 || 400 <= $info['http_code']) {
371
- $info['redirect_url'] = null;
372
-
373
- return null;
374
- }
375
-
376
- try {
377
- $url = self::parseUrl($location);
378
- } catch (InvalidArgumentException $e) {
379
- $info['redirect_url'] = null;
380
-
381
- return null;
382
- }
383
-
384
- $url = self::resolveUrl($url, $info['url']);
385
- $info['redirect_url'] = implode('', $url);
386
-
387
- if ($info['redirect_count'] >= $maxRedirects) {
388
- return null;
389
- }
390
-
391
- $info['url'] = $url;
392
- ++$info['redirect_count'];
393
- $info['redirect_time'] = microtime(true) - $info['start_time'];
394
-
395
- // Do like curl and browsers: turn POST to GET on 301, 302 and 303
396
- if (\in_array($info['http_code'], [301, 302, 303], true)) {
397
- $options = stream_context_get_options($context)['http'];
398
-
399
- if ('POST' === $options['method'] || 303 === $info['http_code']) {
400
- $info['http_method'] = $options['method'] = 'HEAD' === $options['method'] ? 'HEAD' : 'GET';
401
- $options['content'] = '';
402
- $filterContentHeaders = static function ($h) {
403
- return 0 !== stripos($h, 'Content-Length:') && 0 !== stripos($h, 'Content-Type:') && 0 !== stripos($h, 'Transfer-Encoding:');
404
- };
405
- $options['header'] = array_filter($options['header'], $filterContentHeaders);
406
- $redirectHeaders['no_auth'] = array_filter($redirectHeaders['no_auth'], $filterContentHeaders);
407
- $redirectHeaders['with_auth'] = array_filter($redirectHeaders['with_auth'], $filterContentHeaders);
408
-
409
- stream_context_set_option($context, ['http' => $options]);
410
- }
411
- }
412
-
413
- [$host, $port] = self::parseHostPort($url, $info);
414
-
415
- if (false !== (parse_url($location, \PHP_URL_HOST) ?? false)) {
416
- // Authorization and Cookie headers MUST NOT follow except for the initial host name
417
- $requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
418
- $requestHeaders[] = 'Host: '.$host.$port;
419
- $dnsResolve = !self::configureHeadersAndProxy($context, $host, $requestHeaders, $proxy, 'https:' === $url['scheme']);
420
- } else {
421
- $dnsResolve = isset(stream_context_get_options($context)['ssl']['peer_name']);
422
- }
423
-
424
- if ($dnsResolve) {
425
- $ip = self::dnsResolve($host, $multi, $info, $onProgress);
426
- $url['authority'] = substr_replace($url['authority'], $ip, -\strlen($host) - \strlen($port), \strlen($host));
427
- }
428
-
429
- return implode('', $url);
430
- };
431
- }
432
-
433
- private static function configureHeadersAndProxy($context, string $host, array $requestHeaders, ?array $proxy, bool $isSsl): bool
434
- {
435
- if (null === $proxy) {
436
- stream_context_set_option($context, 'http', 'header', $requestHeaders);
437
- stream_context_set_option($context, 'ssl', 'peer_name', $host);
438
-
439
- return false;
440
- }
441
-
442
- // Matching "no_proxy" should follow the behavior of curl
443
-
444
- foreach ($proxy['no_proxy'] as $rule) {
445
- $dotRule = '.'.ltrim($rule, '.');
446
-
447
- if ('*' === $rule || $host === $rule || str_ends_with($host, $dotRule)) {
448
- stream_context_set_option($context, 'http', 'proxy', null);
449
- stream_context_set_option($context, 'http', 'request_fulluri', false);
450
- stream_context_set_option($context, 'http', 'header', $requestHeaders);
451
- stream_context_set_option($context, 'ssl', 'peer_name', $host);
452
-
453
- return false;
454
- }
455
- }
456
-
457
- if (null !== $proxy['auth']) {
458
- $requestHeaders[] = 'Proxy-Authorization: '.$proxy['auth'];
459
- }
460
-
461
- stream_context_set_option($context, 'http', 'proxy', $proxy['url']);
462
- stream_context_set_option($context, 'http', 'request_fulluri', !$isSsl);
463
- stream_context_set_option($context, 'http', 'header', $requestHeaders);
464
- stream_context_set_option($context, 'ssl', 'peer_name', null);
465
-
466
- return true;
467
- }
468
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/NoPrivateNetworkHttpClient.php DELETED
@@ -1,132 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Psr\Log\LoggerAwareInterface;
15
- use Psr\Log\LoggerInterface;
16
- use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
17
- use Symfony\Component\HttpClient\Exception\TransportException;
18
- use Symfony\Component\HttpFoundation\IpUtils;
19
- use Symfony\Contracts\HttpClient\HttpClientInterface;
20
- use Symfony\Contracts\HttpClient\ResponseInterface;
21
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
22
- use Symfony\Contracts\Service\ResetInterface;
23
-
24
- /**
25
- * Decorator that blocks requests to private networks by default.
26
- *
27
- * @author Hallison Boaventura <hallisonboaventura@gmail.com>
28
- */
29
- final class NoPrivateNetworkHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface
30
- {
31
- use HttpClientTrait;
32
-
33
- private const PRIVATE_SUBNETS = [
34
- '127.0.0.0/8',
35
- '10.0.0.0/8',
36
- '192.168.0.0/16',
37
- '172.16.0.0/12',
38
- '169.254.0.0/16',
39
- '0.0.0.0/8',
40
- '240.0.0.0/4',
41
- '::1/128',
42
- 'fc00::/7',
43
- 'fe80::/10',
44
- '::ffff:0:0/96',
45
- '::/128',
46
- ];
47
-
48
- private $client;
49
- private $subnets;
50
-
51
- /**
52
- * @param string|array|null $subnets String or array of subnets using CIDR notation that will be used by IpUtils.
53
- * If null is passed, the standard private subnets will be used.
54
- */
55
- public function __construct(HttpClientInterface $client, $subnets = null)
56
- {
57
- if (!(\is_array($subnets) || \is_string($subnets) || null === $subnets)) {
58
- throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be of the type array, string or null. "%s" given.', __METHOD__, get_debug_type($subnets)));
59
- }
60
-
61
- if (!class_exists(IpUtils::class)) {
62
- throw new \LogicException(sprintf('You cannot use "%s" if the HttpFoundation component is not installed. Try running "composer require symfony/http-foundation".', __CLASS__));
63
- }
64
-
65
- $this->client = $client;
66
- $this->subnets = $subnets;
67
- }
68
-
69
- /**
70
- * {@inheritdoc}
71
- */
72
- public function request(string $method, string $url, array $options = []): ResponseInterface
73
- {
74
- $onProgress = $options['on_progress'] ?? null;
75
- if (null !== $onProgress && !\is_callable($onProgress)) {
76
- throw new InvalidArgumentException(sprintf('Option "on_progress" must be callable, "%s" given.', get_debug_type($onProgress)));
77
- }
78
-
79
- $subnets = $this->subnets;
80
- $lastPrimaryIp = '';
81
-
82
- $options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use ($onProgress, $subnets, &$lastPrimaryIp): void {
83
- if ($info['primary_ip'] !== $lastPrimaryIp) {
84
- if ($info['primary_ip'] && IpUtils::checkIp($info['primary_ip'], $subnets ?? self::PRIVATE_SUBNETS)) {
85
- throw new TransportException(sprintf('IP "%s" is blocked for "%s".', $info['primary_ip'], $info['url']));
86
- }
87
-
88
- $lastPrimaryIp = $info['primary_ip'];
89
- }
90
-
91
- null !== $onProgress && $onProgress($dlNow, $dlSize, $info);
92
- };
93
-
94
- return $this->client->request($method, $url, $options);
95
- }
96
-
97
- /**
98
- * {@inheritdoc}
99
- */
100
- public function stream($responses, float $timeout = null): ResponseStreamInterface
101
- {
102
- return $this->client->stream($responses, $timeout);
103
- }
104
-
105
- /**
106
- * {@inheritdoc}
107
- */
108
- public function setLogger(LoggerInterface $logger): void
109
- {
110
- if ($this->client instanceof LoggerAwareInterface) {
111
- $this->client->setLogger($logger);
112
- }
113
- }
114
-
115
- /**
116
- * {@inheritdoc}
117
- */
118
- public function withOptions(array $options): self
119
- {
120
- $clone = clone $this;
121
- $clone->client = $this->client->withOptions($options);
122
-
123
- return $clone;
124
- }
125
-
126
- public function reset()
127
- {
128
- if ($this->client instanceof ResetInterface) {
129
- $this->client->reset();
130
- }
131
- }
132
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Psr18Client.php DELETED
@@ -1,239 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Http\Discovery\Exception\NotFoundException;
15
- use Http\Discovery\Psr17FactoryDiscovery;
16
- use Nyholm\Psr7\Factory\Psr17Factory;
17
- use Nyholm\Psr7\Request;
18
- use Nyholm\Psr7\Uri;
19
- use Psr\Http\Client\ClientInterface;
20
- use Psr\Http\Client\NetworkExceptionInterface;
21
- use Psr\Http\Client\RequestExceptionInterface;
22
- use Psr\Http\Message\RequestFactoryInterface;
23
- use Psr\Http\Message\RequestInterface;
24
- use Psr\Http\Message\ResponseFactoryInterface;
25
- use Psr\Http\Message\ResponseInterface;
26
- use Psr\Http\Message\StreamFactoryInterface;
27
- use Psr\Http\Message\StreamInterface;
28
- use Psr\Http\Message\UriFactoryInterface;
29
- use Psr\Http\Message\UriInterface;
30
- use Symfony\Component\HttpClient\Response\StreamableInterface;
31
- use Symfony\Component\HttpClient\Response\StreamWrapper;
32
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
33
- use Symfony\Contracts\HttpClient\HttpClientInterface;
34
- use Symfony\Contracts\Service\ResetInterface;
35
-
36
- if (!interface_exists(RequestFactoryInterface::class)) {
37
- throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as the "psr/http-factory" package is not installed. Try running "composer require nyholm/psr7".');
38
- }
39
-
40
- if (!interface_exists(ClientInterface::class)) {
41
- throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as the "psr/http-client" package is not installed. Try running "composer require psr/http-client".');
42
- }
43
-
44
- /**
45
- * An adapter to turn a Symfony HttpClientInterface into a PSR-18 ClientInterface.
46
- *
47
- * Run "composer require psr/http-client" to install the base ClientInterface. Run
48
- * "composer require nyholm/psr7" to install an efficient implementation of response
49
- * and stream factories with flex-provided autowiring aliases.
50
- *
51
- * @author Nicolas Grekas <p@tchwork.com>
52
- */
53
- final class Psr18Client implements ClientInterface, RequestFactoryInterface, StreamFactoryInterface, UriFactoryInterface, ResetInterface
54
- {
55
- private $client;
56
- private $responseFactory;
57
- private $streamFactory;
58
-
59
- public function __construct(HttpClientInterface $client = null, ResponseFactoryInterface $responseFactory = null, StreamFactoryInterface $streamFactory = null)
60
- {
61
- $this->client = $client ?? HttpClient::create();
62
- $this->responseFactory = $responseFactory;
63
- $this->streamFactory = $streamFactory ?? ($responseFactory instanceof StreamFactoryInterface ? $responseFactory : null);
64
-
65
- if (null !== $this->responseFactory && null !== $this->streamFactory) {
66
- return;
67
- }
68
-
69
- if (!class_exists(Psr17Factory::class) && !class_exists(Psr17FactoryDiscovery::class)) {
70
- throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as no PSR-17 factories have been provided. Try running "composer require nyholm/psr7".');
71
- }
72
-
73
- try {
74
- $psr17Factory = class_exists(Psr17Factory::class, false) ? new Psr17Factory() : null;
75
- $this->responseFactory = $this->responseFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findResponseFactory();
76
- $this->streamFactory = $this->streamFactory ?? $psr17Factory ?? Psr17FactoryDiscovery::findStreamFactory();
77
- } catch (NotFoundException $e) {
78
- throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\HttplugClient" as no PSR-17 factories have been found. Try running "composer require nyholm/psr7".', 0, $e);
79
- }
80
- }
81
-
82
- /**
83
- * {@inheritdoc}
84
- */
85
- public function sendRequest(RequestInterface $request): ResponseInterface
86
- {
87
- try {
88
- $body = $request->getBody();
89
-
90
- if ($body->isSeekable()) {
91
- $body->seek(0);
92
- }
93
-
94
- $response = $this->client->request($request->getMethod(), (string) $request->getUri(), [
95
- 'headers' => $request->getHeaders(),
96
- 'body' => $body->getContents(),
97
- 'http_version' => '1.0' === $request->getProtocolVersion() ? '1.0' : null,
98
- ]);
99
-
100
- $psrResponse = $this->responseFactory->createResponse($response->getStatusCode());
101
-
102
- foreach ($response->getHeaders(false) as $name => $values) {
103
- foreach ($values as $value) {
104
- $psrResponse = $psrResponse->withAddedHeader($name, $value);
105
- }
106
- }
107
-
108
- $body = $response instanceof StreamableInterface ? $response->toStream(false) : StreamWrapper::createResource($response, $this->client);
109
- $body = $this->streamFactory->createStreamFromResource($body);
110
-
111
- if ($body->isSeekable()) {
112
- $body->seek(0);
113
- }
114
-
115
- return $psrResponse->withBody($body);
116
- } catch (TransportExceptionInterface $e) {
117
- if ($e instanceof \InvalidArgumentException) {
118
- throw new Psr18RequestException($e, $request);
119
- }
120
-
121
- throw new Psr18NetworkException($e, $request);
122
- }
123
- }
124
-
125
- /**
126
- * {@inheritdoc}
127
- */
128
- public function createRequest(string $method, $uri): RequestInterface
129
- {
130
- if ($this->responseFactory instanceof RequestFactoryInterface) {
131
- return $this->responseFactory->createRequest($method, $uri);
132
- }
133
-
134
- if (class_exists(Request::class)) {
135
- return new Request($method, $uri);
136
- }
137
-
138
- if (class_exists(Psr17FactoryDiscovery::class)) {
139
- return Psr17FactoryDiscovery::findRequestFactory()->createRequest($method, $uri);
140
- }
141
-
142
- throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
143
- }
144
-
145
- /**
146
- * {@inheritdoc}
147
- */
148
- public function createStream(string $content = ''): StreamInterface
149
- {
150
- $stream = $this->streamFactory->createStream($content);
151
-
152
- if ($stream->isSeekable()) {
153
- $stream->seek(0);
154
- }
155
-
156
- return $stream;
157
- }
158
-
159
- /**
160
- * {@inheritdoc}
161
- */
162
- public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface
163
- {
164
- return $this->streamFactory->createStreamFromFile($filename, $mode);
165
- }
166
-
167
- /**
168
- * {@inheritdoc}
169
- */
170
- public function createStreamFromResource($resource): StreamInterface
171
- {
172
- return $this->streamFactory->createStreamFromResource($resource);
173
- }
174
-
175
- /**
176
- * {@inheritdoc}
177
- */
178
- public function createUri(string $uri = ''): UriInterface
179
- {
180
- if ($this->responseFactory instanceof UriFactoryInterface) {
181
- return $this->responseFactory->createUri($uri);
182
- }
183
-
184
- if (class_exists(Uri::class)) {
185
- return new Uri($uri);
186
- }
187
-
188
- if (class_exists(Psr17FactoryDiscovery::class)) {
189
- return Psr17FactoryDiscovery::findUrlFactory()->createUri($uri);
190
- }
191
-
192
- throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__));
193
- }
194
-
195
- public function reset()
196
- {
197
- if ($this->client instanceof ResetInterface) {
198
- $this->client->reset();
199
- }
200
- }
201
- }
202
-
203
- /**
204
- * @internal
205
- */
206
- class Psr18NetworkException extends \RuntimeException implements NetworkExceptionInterface
207
- {
208
- private $request;
209
-
210
- public function __construct(TransportExceptionInterface $e, RequestInterface $request)
211
- {
212
- parent::__construct($e->getMessage(), 0, $e);
213
- $this->request = $request;
214
- }
215
-
216
- public function getRequest(): RequestInterface
217
- {
218
- return $this->request;
219
- }
220
- }
221
-
222
- /**
223
- * @internal
224
- */
225
- class Psr18RequestException extends \InvalidArgumentException implements RequestExceptionInterface
226
- {
227
- private $request;
228
-
229
- public function __construct(TransportExceptionInterface $e, RequestInterface $request)
230
- {
231
- parent::__construct($e->getMessage(), 0, $e);
232
- $this->request = $request;
233
- }
234
-
235
- public function getRequest(): RequestInterface
236
- {
237
- return $this->request;
238
- }
239
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/README.md DELETED
@@ -1,27 +0,0 @@
1
- HttpClient component
2
- ====================
3
-
4
- The HttpClient component provides powerful methods to fetch HTTP resources synchronously or asynchronously.
5
-
6
- Sponsor
7
- -------
8
-
9
- The Httpclient component for Symfony 5.4/6.0 is [backed][1] by [Klaxoon][2].
10
-
11
- Klaxoon is a platform that empowers organizations to run effective and
12
- productive workshops easily in a hybrid environment. Anytime, Anywhere.
13
-
14
- Help Symfony by [sponsoring][3] its development!
15
-
16
- Resources
17
- ---------
18
-
19
- * [Documentation](https://symfony.com/doc/current/components/http_client.html)
20
- * [Contributing](https://symfony.com/doc/current/contributing/index.html)
21
- * [Report issues](https://github.com/symfony/symfony/issues) and
22
- [send Pull Requests](https://github.com/symfony/symfony/pulls)
23
- in the [main Symfony repository](https://github.com/symfony/symfony)
24
-
25
- [1]: https://symfony.com/backers
26
- [2]: https://klaxoon.com
27
- [3]: https://symfony.com/sponsor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/AmpResponse.php DELETED
@@ -1,461 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Amp\ByteStream\StreamException;
15
- use Amp\CancellationTokenSource;
16
- use Amp\Coroutine;
17
- use Amp\Deferred;
18
- use Amp\Http\Client\HttpException;
19
- use Amp\Http\Client\Request;
20
- use Amp\Http\Client\Response;
21
- use Amp\Loop;
22
- use Amp\Promise;
23
- use Amp\Success;
24
- use Psr\Log\LoggerInterface;
25
- use Symfony\Component\HttpClient\Chunk\FirstChunk;
26
- use Symfony\Component\HttpClient\Chunk\InformationalChunk;
27
- use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
28
- use Symfony\Component\HttpClient\Exception\TransportException;
29
- use Symfony\Component\HttpClient\HttpClientTrait;
30
- use Symfony\Component\HttpClient\Internal\AmpBody;
31
- use Symfony\Component\HttpClient\Internal\AmpClientState;
32
- use Symfony\Component\HttpClient\Internal\Canary;
33
- use Symfony\Component\HttpClient\Internal\ClientState;
34
- use Symfony\Contracts\HttpClient\ResponseInterface;
35
-
36
- /**
37
- * @author Nicolas Grekas <p@tchwork.com>
38
- *
39
- * @internal
40
- */
41
- final class AmpResponse implements ResponseInterface, StreamableInterface
42
- {
43
- use CommonResponseTrait;
44
- use TransportResponseTrait;
45
-
46
- private static $nextId = 'a';
47
-
48
- private $multi;
49
- private $options;
50
- private $canceller;
51
- private $onProgress;
52
-
53
- private static $delay;
54
-
55
- /**
56
- * @internal
57
- */
58
- public function __construct(AmpClientState $multi, Request $request, array $options, ?LoggerInterface $logger)
59
- {
60
- $this->multi = $multi;
61
- $this->options = &$options;
62
- $this->logger = $logger;
63
- $this->timeout = $options['timeout'];
64
- $this->shouldBuffer = $options['buffer'];
65
-
66
- if ($this->inflate = \extension_loaded('zlib') && !$request->hasHeader('accept-encoding')) {
67
- $request->setHeader('Accept-Encoding', 'gzip');
68
- }
69
-
70
- $this->initializer = static function (self $response) {
71
- return null !== $response->options;
72
- };
73
-
74
- $info = &$this->info;
75
- $headers = &$this->headers;
76
- $canceller = $this->canceller = new CancellationTokenSource();
77
- $handle = &$this->handle;
78
-
79
- $info['url'] = (string) $request->getUri();
80
- $info['http_method'] = $request->getMethod();
81
- $info['start_time'] = null;
82
- $info['redirect_url'] = null;
83
- $info['redirect_time'] = 0.0;
84
- $info['redirect_count'] = 0;
85
- $info['size_upload'] = 0.0;
86
- $info['size_download'] = 0.0;
87
- $info['upload_content_length'] = -1.0;
88
- $info['download_content_length'] = -1.0;
89
- $info['user_data'] = $options['user_data'];
90
- $info['max_duration'] = $options['max_duration'];
91
- $info['debug'] = '';
92
-
93
- $onProgress = $options['on_progress'] ?? static function () {};
94
- $onProgress = $this->onProgress = static function () use (&$info, $onProgress) {
95
- $info['total_time'] = microtime(true) - $info['start_time'];
96
- $onProgress((int) $info['size_download'], ((int) (1 + $info['download_content_length']) ?: 1) - 1, (array) $info);
97
- };
98
-
99
- $pauseDeferred = new Deferred();
100
- $pause = new Success();
101
-
102
- $throttleWatcher = null;
103
-
104
- $this->id = $id = self::$nextId++;
105
- Loop::defer(static function () use ($request, $multi, &$id, &$info, &$headers, $canceller, &$options, $onProgress, &$handle, $logger, &$pause) {
106
- return new Coroutine(self::generateResponse($request, $multi, $id, $info, $headers, $canceller, $options, $onProgress, $handle, $logger, $pause));
107
- });
108
-
109
- $info['pause_handler'] = static function (float $duration) use (&$throttleWatcher, &$pauseDeferred, &$pause) {
110
- if (null !== $throttleWatcher) {
111
- Loop::cancel($throttleWatcher);
112
- }
113
-
114
- $pause = $pauseDeferred->promise();
115
-
116
- if ($duration <= 0) {
117
- $deferred = $pauseDeferred;
118
- $pauseDeferred = new Deferred();
119
- $deferred->resolve();
120
- } else {
121
- $throttleWatcher = Loop::delay(ceil(1000 * $duration), static function () use (&$pauseDeferred) {
122
- $deferred = $pauseDeferred;
123
- $pauseDeferred = new Deferred();
124
- $deferred->resolve();
125
- });
126
- }
127
- };
128
-
129
- $multi->lastTimeout = null;
130
- $multi->openHandles[$id] = $id;
131
- ++$multi->responseCount;
132
-
133
- $this->canary = new Canary(static function () use ($canceller, $multi, $id) {
134
- $canceller->cancel();
135
- unset($multi->openHandles[$id], $multi->handlesActivity[$id]);
136
- });
137
- }
138
-
139
- /**
140
- * {@inheritdoc}
141
- */
142
- public function getInfo(string $type = null)
143
- {
144
- return null !== $type ? $this->info[$type] ?? null : $this->info;
145
- }
146
-
147
- public function __sleep(): array
148
- {
149
- throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
150
- }
151
-
152
- public function __wakeup()
153
- {
154
- throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
155
- }
156
-
157
- public function __destruct()
158
- {
159
- try {
160
- $this->doDestruct();
161
- } finally {
162
- // Clear the DNS cache when all requests completed
163
- if (0 >= --$this->multi->responseCount) {
164
- $this->multi->responseCount = 0;
165
- $this->multi->dnsCache = [];
166
- }
167
- }
168
- }
169
-
170
- /**
171
- * {@inheritdoc}
172
- */
173
- private static function schedule(self $response, array &$runningResponses): void
174
- {
175
- if (isset($runningResponses[0])) {
176
- $runningResponses[0][1][$response->id] = $response;
177
- } else {
178
- $runningResponses[0] = [$response->multi, [$response->id => $response]];
179
- }
180
-
181
- if (!isset($response->multi->openHandles[$response->id])) {
182
- $response->multi->handlesActivity[$response->id][] = null;
183
- $response->multi->handlesActivity[$response->id][] = null !== $response->info['error'] ? new TransportException($response->info['error']) : null;
184
- }
185
- }
186
-
187
- /**
188
- * {@inheritdoc}
189
- *
190
- * @param AmpClientState $multi
191
- */
192
- private static function perform(ClientState $multi, array &$responses = null): void
193
- {
194
- if ($responses) {
195
- foreach ($responses as $response) {
196
- try {
197
- if ($response->info['start_time']) {
198
- $response->info['total_time'] = microtime(true) - $response->info['start_time'];
199
- ($response->onProgress)();
200
- }
201
- } catch (\Throwable $e) {
202
- $multi->handlesActivity[$response->id][] = null;
203
- $multi->handlesActivity[$response->id][] = $e;
204
- }
205
- }
206
- }
207
- }
208
-
209
- /**
210
- * {@inheritdoc}
211
- *
212
- * @param AmpClientState $multi
213
- */
214
- private static function select(ClientState $multi, float $timeout): int
215
- {
216
- $timeout += microtime(true);
217
- self::$delay = Loop::defer(static function () use ($timeout) {
218
- if (0 < $timeout -= microtime(true)) {
219
- self::$delay = Loop::delay(ceil(1000 * $timeout), [Loop::class, 'stop']);
220
- } else {
221
- Loop::stop();
222
- }
223
- });
224
-
225
- Loop::run();
226
-
227
- return null === self::$delay ? 1 : 0;
228
- }
229
-
230
- private static function generateResponse(Request $request, AmpClientState $multi, string $id, array &$info, array &$headers, CancellationTokenSource $canceller, array &$options, \Closure $onProgress, &$handle, ?LoggerInterface $logger, Promise &$pause)
231
- {
232
- $request->setInformationalResponseHandler(static function (Response $response) use ($multi, $id, &$info, &$headers) {
233
- self::addResponseHeaders($response, $info, $headers);
234
- $multi->handlesActivity[$id][] = new InformationalChunk($response->getStatus(), $response->getHeaders());
235
- self::stopLoop();
236
- });
237
-
238
- try {
239
- /* @var Response $response */
240
- if (null === $response = yield from self::getPushedResponse($request, $multi, $info, $headers, $options, $logger)) {
241
- $logger && $logger->info(sprintf('Request: "%s %s"', $info['http_method'], $info['url']));
242
-
243
- $response = yield from self::followRedirects($request, $multi, $info, $headers, $canceller, $options, $onProgress, $handle, $logger, $pause);
244
- }
245
-
246
- $options = null;
247
-
248
- $multi->handlesActivity[$id][] = new FirstChunk();
249
-
250
- if ('HEAD' === $response->getRequest()->getMethod() || \in_array($info['http_code'], [204, 304], true)) {
251
- $multi->handlesActivity[$id][] = null;
252
- $multi->handlesActivity[$id][] = null;
253
- self::stopLoop();
254
-
255
- return;
256
- }
257
-
258
- if ($response->hasHeader('content-length')) {
259
- $info['download_content_length'] = (float) $response->getHeader('content-length');
260
- }
261
-
262
- $body = $response->getBody();
263
-
264
- while (true) {
265
- self::stopLoop();
266
-
267
- yield $pause;
268
-
269
- if (null === $data = yield $body->read()) {
270
- break;
271
- }
272
-
273
- $info['size_download'] += \strlen($data);
274
- $multi->handlesActivity[$id][] = $data;
275
- }
276
-
277
- $multi->handlesActivity[$id][] = null;
278
- $multi->handlesActivity[$id][] = null;
279
- } catch (\Throwable $e) {
280
- $multi->handlesActivity[$id][] = null;
281
- $multi->handlesActivity[$id][] = $e;
282
- } finally {
283
- $info['download_content_length'] = $info['size_download'];
284
- }
285
-
286
- self::stopLoop();
287
- }
288
-
289
- private static function followRedirects(Request $originRequest, AmpClientState $multi, array &$info, array &$headers, CancellationTokenSource $canceller, array $options, \Closure $onProgress, &$handle, ?LoggerInterface $logger, Promise &$pause)
290
- {
291
- yield $pause;
292
-
293
- $originRequest->setBody(new AmpBody($options['body'], $info, $onProgress));
294
- $response = yield $multi->request($options, $originRequest, $canceller->getToken(), $info, $onProgress, $handle);
295
- $previousUrl = null;
296
-
297
- while (true) {
298
- self::addResponseHeaders($response, $info, $headers);
299
- $status = $response->getStatus();
300
-
301
- if (!\in_array($status, [301, 302, 303, 307, 308], true) || null === $location = $response->getHeader('location')) {
302
- return $response;
303
- }
304
-
305
- $urlResolver = new class() {
306
- use HttpClientTrait {
307
- parseUrl as public;
308
- resolveUrl as public;
309
- }
310
- };
311
-
312
- try {
313
- $previousUrl = $previousUrl ?? $urlResolver::parseUrl($info['url']);
314
- $location = $urlResolver::parseUrl($location);
315
- $location = $urlResolver::resolveUrl($location, $previousUrl);
316
- $info['redirect_url'] = implode('', $location);
317
- } catch (InvalidArgumentException $e) {
318
- return $response;
319
- }
320
-
321
- if (0 >= $options['max_redirects'] || $info['redirect_count'] >= $options['max_redirects']) {
322
- return $response;
323
- }
324
-
325
- $logger && $logger->info(sprintf('Redirecting: "%s %s"', $status, $info['url']));
326
-
327
- try {
328
- // Discard body of redirects
329
- while (null !== yield $response->getBody()->read()) {
330
- }
331
- } catch (HttpException|StreamException $e) {
332
- // Ignore streaming errors on previous responses
333
- }
334
-
335
- ++$info['redirect_count'];
336
- $info['url'] = $info['redirect_url'];
337
- $info['redirect_url'] = null;
338
- $previousUrl = $location;
339
-
340
- $request = new Request($info['url'], $info['http_method']);
341
- $request->setProtocolVersions($originRequest->getProtocolVersions());
342
- $request->setTcpConnectTimeout($originRequest->getTcpConnectTimeout());
343
- $request->setTlsHandshakeTimeout($originRequest->getTlsHandshakeTimeout());
344
- $request->setTransferTimeout($originRequest->getTransferTimeout());
345
-
346
- if (\in_array($status, [301, 302, 303], true)) {
347
- $originRequest->removeHeader('transfer-encoding');
348
- $originRequest->removeHeader('content-length');
349
- $originRequest->removeHeader('content-type');
350
-
351
- // Do like curl and browsers: turn POST to GET on 301, 302 and 303
352
- if ('POST' === $response->getRequest()->getMethod() || 303 === $status) {
353
- $info['http_method'] = 'HEAD' === $response->getRequest()->getMethod() ? 'HEAD' : 'GET';
354
- $request->setMethod($info['http_method']);
355
- }
356
- } else {
357
- $request->setBody(AmpBody::rewind($response->getRequest()->getBody()));
358
- }
359
-
360
- foreach ($originRequest->getRawHeaders() as [$name, $value]) {
361
- $request->setHeader($name, $value);
362
- }
363
-
364
- if ($request->getUri()->getAuthority() !== $originRequest->getUri()->getAuthority()) {
365
- $request->removeHeader('authorization');
366
- $request->removeHeader('cookie');
367
- $request->removeHeader('host');
368
- }
369
-
370
- yield $pause;
371
-
372
- $response = yield $multi->request($options, $request, $canceller->getToken(), $info, $onProgress, $handle);
373
- $info['redirect_time'] = microtime(true) - $info['start_time'];
374
- }
375
- }
376
-
377
- private static function addResponseHeaders(Response $response, array &$info, array &$headers): void
378
- {
379
- $info['http_code'] = $response->getStatus();
380
-
381
- if ($headers) {
382
- $info['debug'] .= "< \r\n";
383
- $headers = [];
384
- }
385
-
386
- $h = sprintf('HTTP/%s %s %s', $response->getProtocolVersion(), $response->getStatus(), $response->getReason());
387
- $info['debug'] .= "< {$h}\r\n";
388
- $info['response_headers'][] = $h;
389
-
390
- foreach ($response->getRawHeaders() as [$name, $value]) {
391
- $headers[strtolower($name)][] = $value;
392
- $h = $name.': '.$value;
393
- $info['debug'] .= "< {$h}\r\n";
394
- $info['response_headers'][] = $h;
395
- }
396
-
397
- $info['debug'] .= "< \r\n";
398
- }
399
-
400
- /**
401
- * Accepts pushed responses only if their headers related to authentication match the request.
402
- */
403
- private static function getPushedResponse(Request $request, AmpClientState $multi, array &$info, array &$headers, array $options, ?LoggerInterface $logger)
404
- {
405
- if ('' !== $options['body']) {
406
- return null;
407
- }
408
-
409
- $authority = $request->getUri()->getAuthority();
410
-
411
- foreach ($multi->pushedResponses[$authority] ?? [] as $i => [$pushedUrl, $pushDeferred, $pushedRequest, $pushedResponse, $parentOptions]) {
412
- if ($info['url'] !== $pushedUrl || $info['http_method'] !== $pushedRequest->getMethod()) {
413
- continue;
414
- }
415
-
416
- foreach ($parentOptions as $k => $v) {
417
- if ($options[$k] !== $v) {
418
- continue 2;
419
- }
420
- }
421
-
422
- foreach (['authorization', 'cookie', 'range', 'proxy-authorization'] as $k) {
423
- if ($pushedRequest->getHeaderArray($k) !== $request->getHeaderArray($k)) {
424
- continue 2;
425
- }
426
- }
427
-
428
- $response = yield $pushedResponse;
429
-
430
- foreach ($response->getHeaderArray('vary') as $vary) {
431
- foreach (preg_split('/\s*+,\s*+/', $vary) as $v) {
432
- if ('*' === $v || ($pushedRequest->getHeaderArray($v) !== $request->getHeaderArray($v) && 'accept-encoding' !== strtolower($v))) {
433
- $logger && $logger->debug(sprintf('Skipping pushed response: "%s"', $info['url']));
434
- continue 3;
435
- }
436
- }
437
- }
438
-
439
- $pushDeferred->resolve();
440
- $logger && $logger->debug(sprintf('Accepting pushed response: "%s %s"', $info['http_method'], $info['url']));
441
- self::addResponseHeaders($response, $info, $headers);
442
- unset($multi->pushedResponses[$authority][$i]);
443
-
444
- if (!$multi->pushedResponses[$authority]) {
445
- unset($multi->pushedResponses[$authority]);
446
- }
447
-
448
- return $response;
449
- }
450
- }
451
-
452
- private static function stopLoop(): void
453
- {
454
- if (null !== self::$delay) {
455
- Loop::cancel(self::$delay);
456
- self::$delay = null;
457
- }
458
-
459
- Loop::defer([Loop::class, 'stop']);
460
- }
461
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/AsyncContext.php DELETED
@@ -1,189 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Symfony\Component\HttpClient\Chunk\DataChunk;
15
- use Symfony\Component\HttpClient\Chunk\LastChunk;
16
- use Symfony\Component\HttpClient\Exception\TransportException;
17
- use Symfony\Contracts\HttpClient\ChunkInterface;
18
- use Symfony\Contracts\HttpClient\HttpClientInterface;
19
- use Symfony\Contracts\HttpClient\ResponseInterface;
20
-
21
- /**
22
- * A DTO to work with AsyncResponse.
23
- *
24
- * @author Nicolas Grekas <p@tchwork.com>
25
- */
26
- final class AsyncContext
27
- {
28
- private $passthru;
29
- private $client;
30
- private $response;
31
- private $info = [];
32
- private $content;
33
- private $offset;
34
-
35
- public function __construct(&$passthru, HttpClientInterface $client, ResponseInterface &$response, array &$info, $content, int $offset)
36
- {
37
- $this->passthru = &$passthru;
38
- $this->client = $client;
39
- $this->response = &$response;
40
- $this->info = &$info;
41
- $this->content = $content;
42
- $this->offset = $offset;
43
- }
44
-
45
- /**
46
- * Returns the HTTP status without consuming the response.
47
- */
48
- public function getStatusCode(): int
49
- {
50
- return $this->response->getInfo('http_code');
51
- }
52
-
53
- /**
54
- * Returns the headers without consuming the response.
55
- */
56
- public function getHeaders(): array
57
- {
58
- $headers = [];
59
-
60
- foreach ($this->response->getInfo('response_headers') as $h) {
61
- if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? ([123456789]\d\d)(?: |$)#', $h, $m)) {
62
- $headers = [];
63
- } elseif (2 === \count($m = explode(':', $h, 2))) {
64
- $headers[strtolower($m[0])][] = ltrim($m[1]);
65
- }
66
- }
67
-
68
- return $headers;
69
- }
70
-
71
- /**
72
- * @return resource|null The PHP stream resource where the content is buffered, if it is
73
- */
74
- public function getContent()
75
- {
76
- return $this->content;
77
- }
78
-
79
- /**
80
- * Creates a new chunk of content.
81
- */
82
- public function createChunk(string $data): ChunkInterface
83
- {
84
- return new DataChunk($this->offset, $data);
85
- }
86
-
87
- /**
88
- * Pauses the request for the given number of seconds.
89
- */
90
- public function pause(float $duration): void
91
- {
92
- if (\is_callable($pause = $this->response->getInfo('pause_handler'))) {
93
- $pause($duration);
94
- } elseif (0 < $duration) {
95
- usleep(1E6 * $duration);
96
- }
97
- }
98
-
99
- /**
100
- * Cancels the request and returns the last chunk to yield.
101
- */
102
- public function cancel(): ChunkInterface
103
- {
104
- $this->info['canceled'] = true;
105
- $this->info['error'] = 'Response has been canceled.';
106
- $this->response->cancel();
107
-
108
- return new LastChunk();
109
- }
110
-
111
- /**
112
- * Returns the current info of the response.
113
- */
114
- public function getInfo(string $type = null)
115
- {
116
- if (null !== $type) {
117
- return $this->info[$type] ?? $this->response->getInfo($type);
118
- }
119
-
120
- return $this->info + $this->response->getInfo();
121
- }
122
-
123
- /**
124
- * Attaches an info to the response.
125
- *
126
- * @return $this
127
- */
128
- public function setInfo(string $type, $value): self
129
- {
130
- if ('canceled' === $type && $value !== $this->info['canceled']) {
131
- throw new \LogicException('You cannot set the "canceled" info directly.');
132
- }
133
-
134
- if (null === $value) {
135
- unset($this->info[$type]);
136
- } else {
137
- $this->info[$type] = $value;
138
- }
139
-
140
- return $this;
141
- }
142
-
143
- /**
144
- * Returns the currently processed response.
145
- */
146
- public function getResponse(): ResponseInterface
147
- {
148
- return $this->response;
149
- }
150
-
151
- /**
152
- * Replaces the currently processed response by doing a new request.
153
- */
154
- public function replaceRequest(string $method, string $url, array $options = []): ResponseInterface
155
- {
156
- $this->info['previous_info'][] = $info = $this->response->getInfo();
157
- if (null !== $onProgress = $options['on_progress'] ?? null) {
158
- $thisInfo = &$this->info;
159
- $options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
160
- $onProgress($dlNow, $dlSize, $thisInfo + $info);
161
- };
162
- }
163
- if (0 < ($info['max_duration'] ?? 0) && 0 < ($info['total_time'] ?? 0)) {
164
- if (0 >= $options['max_duration'] = $info['max_duration'] - $info['total_time']) {
165
- throw new TransportException(sprintf('Max duration was reached for "%s".', $info['url']));
166
- }
167
- }
168
-
169
- return $this->response = $this->client->request($method, $url, ['buffer' => false] + $options);
170
- }
171
-
172
- /**
173
- * Replaces the currently processed response by another one.
174
- */
175
- public function replaceResponse(ResponseInterface $response): ResponseInterface
176
- {
177
- $this->info['previous_info'][] = $this->response->getInfo();
178
-
179
- return $this->response = $response;
180
- }
181
-
182
- /**
183
- * Replaces or removes the chunk filter iterator.
184
- */
185
- public function passthru(callable $passthru = null): void
186
- {
187
- $this->passthru = $passthru;
188
- }
189
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/AsyncResponse.php DELETED
@@ -1,478 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Symfony\Component\HttpClient\Chunk\ErrorChunk;
15
- use Symfony\Component\HttpClient\Chunk\FirstChunk;
16
- use Symfony\Component\HttpClient\Chunk\LastChunk;
17
- use Symfony\Component\HttpClient\Exception\TransportException;
18
- use Symfony\Contracts\HttpClient\ChunkInterface;
19
- use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
20
- use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
21
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
22
- use Symfony\Contracts\HttpClient\HttpClientInterface;
23
- use Symfony\Contracts\HttpClient\ResponseInterface;
24
-
25
- /**
26
- * Provides a single extension point to process a response's content stream.
27
- *
28
- * @author Nicolas Grekas <p@tchwork.com>
29
- */
30
- final class AsyncResponse implements ResponseInterface, StreamableInterface
31
- {
32
- use CommonResponseTrait;
33
-
34
- private const FIRST_CHUNK_YIELDED = 1;
35
- private const LAST_CHUNK_YIELDED = 2;
36
-
37
- private $client;
38
- private $response;
39
- private $info = ['canceled' => false];
40
- private $passthru;
41
- private $stream;
42
- private $yieldedState;
43
-
44
- /**
45
- * @param ?callable(ChunkInterface, AsyncContext): ?\Iterator $passthru
46
- */
47
- public function __construct(HttpClientInterface $client, string $method, string $url, array $options, callable $passthru = null)
48
- {
49
- $this->client = $client;
50
- $this->shouldBuffer = $options['buffer'] ?? true;
51
-
52
- if (null !== $onProgress = $options['on_progress'] ?? null) {
53
- $thisInfo = &$this->info;
54
- $options['on_progress'] = static function (int $dlNow, int $dlSize, array $info) use (&$thisInfo, $onProgress) {
55
- $onProgress($dlNow, $dlSize, $thisInfo + $info);
56
- };
57
- }
58
- $this->response = $client->request($method, $url, ['buffer' => false] + $options);
59
- $this->passthru = $passthru;
60
- $this->initializer = static function (self $response, float $timeout = null) {
61
- if (null === $response->shouldBuffer) {
62
- return false;
63
- }
64
-
65
- while (true) {
66
- foreach (self::stream([$response], $timeout) as $chunk) {
67
- if ($chunk->isTimeout() && $response->passthru) {
68
- foreach (self::passthru($response->client, $response, new ErrorChunk($response->offset, new TransportException($chunk->getError()))) as $chunk) {
69
- if ($chunk->isFirst()) {
70
- return false;
71
- }
72
- }
73
-
74
- continue 2;
75
- }
76
-
77
- if ($chunk->isFirst()) {
78
- return false;
79
- }
80
- }
81
-
82
- return false;
83
- }
84
- };
85
- if (\array_key_exists('user_data', $options)) {
86
- $this->info['user_data'] = $options['user_data'];
87
- }
88
- if (\array_key_exists('max_duration', $options)) {
89
- $this->info['max_duration'] = $options['max_duration'];
90
- }
91
- }
92
-
93
- public function getStatusCode(): int
94
- {
95
- if ($this->initializer) {
96
- self::initialize($this);
97
- }
98
-
99
- return $this->response->getStatusCode();
100
- }
101
-
102
- public function getHeaders(bool $throw = true): array
103
- {
104
- if ($this->initializer) {
105
- self::initialize($this);
106
- }
107
-
108
- $headers = $this->response->getHeaders(false);
109
-
110
- if ($throw) {
111
- $this->checkStatusCode();
112
- }
113
-
114
- return $headers;
115
- }
116
-
117
- public function getInfo(string $type = null)
118
- {
119
- if (null !== $type) {
120
- return $this->info[$type] ?? $this->response->getInfo($type);
121
- }
122
-
123
- return $this->info + $this->response->getInfo();
124
- }
125
-
126
- /**
127
- * {@inheritdoc}
128
- */
129
- public function toStream(bool $throw = true)
130
- {
131
- if ($throw) {
132
- // Ensure headers arrived
133
- $this->getHeaders(true);
134
- }
135
-
136
- $handle = function () {
137
- $stream = $this->response instanceof StreamableInterface ? $this->response->toStream(false) : StreamWrapper::createResource($this->response);
138
-
139
- return stream_get_meta_data($stream)['wrapper_data']->stream_cast(\STREAM_CAST_FOR_SELECT);
140
- };
141
-
142
- $stream = StreamWrapper::createResource($this);
143
- stream_get_meta_data($stream)['wrapper_data']
144
- ->bindHandles($handle, $this->content);
145
-
146
- return $stream;
147
- }
148
-
149
- /**
150
- * {@inheritdoc}
151
- */
152
- public function cancel(): void
153
- {
154
- if ($this->info['canceled']) {
155
- return;
156
- }
157
-
158
- $this->info['canceled'] = true;
159
- $this->info['error'] = 'Response has been canceled.';
160
- $this->close();
161
- $client = $this->client;
162
- $this->client = null;
163
-
164
- if (!$this->passthru) {
165
- return;
166
- }
167
-
168
- try {
169
- foreach (self::passthru($client, $this, new LastChunk()) as $chunk) {
170
- // no-op
171
- }
172
-
173
- $this->passthru = null;
174
- } catch (ExceptionInterface $e) {
175
- // ignore any errors when canceling
176
- }
177
- }
178
-
179
- public function __destruct()
180
- {
181
- $httpException = null;
182
-
183
- if ($this->initializer && null === $this->getInfo('error')) {
184
- try {
185
- self::initialize($this, -0.0);
186
- $this->getHeaders(true);
187
- } catch (HttpExceptionInterface $httpException) {
188
- // no-op
189
- }
190
- }
191
-
192
- if ($this->passthru && null === $this->getInfo('error')) {
193
- $this->info['canceled'] = true;
194
-
195
- try {
196
- foreach (self::passthru($this->client, $this, new LastChunk()) as $chunk) {
197
- // no-op
198
- }
199
- } catch (ExceptionInterface $e) {
200
- // ignore any errors when destructing
201
- }
202
- }
203
-
204
- if (null !== $httpException) {
205
- throw $httpException;
206
- }
207
- }
208
-
209
- /**
210
- * @internal
211
- */
212
- public static function stream(iterable $responses, float $timeout = null, string $class = null): \Generator
213
- {
214
- while ($responses) {
215
- $wrappedResponses = [];
216
- $asyncMap = new \SplObjectStorage();
217
- $client = null;
218
-
219
- foreach ($responses as $r) {
220
- if (!$r instanceof self) {
221
- throw new \TypeError(sprintf('"%s::stream()" expects parameter 1 to be an iterable of AsyncResponse objects, "%s" given.', $class ?? static::class, get_debug_type($r)));
222
- }
223
-
224
- if (null !== $e = $r->info['error'] ?? null) {
225
- yield $r => $chunk = new ErrorChunk($r->offset, new TransportException($e));
226
- $chunk->didThrow() ?: $chunk->getContent();
227
- continue;
228
- }
229
-
230
- if (null === $client) {
231
- $client = $r->client;
232
- } elseif ($r->client !== $client) {
233
- throw new TransportException('Cannot stream AsyncResponse objects with many clients.');
234
- }
235
-
236
- $asyncMap[$r->response] = $r;
237
- $wrappedResponses[] = $r->response;
238
-
239
- if ($r->stream) {
240
- yield from self::passthruStream($response = $r->response, $r, new FirstChunk(), $asyncMap);
241
-
242
- if (!isset($asyncMap[$response])) {
243
- array_pop($wrappedResponses);
244
- }
245
-
246
- if ($r->response !== $response && !isset($asyncMap[$r->response])) {
247
- $asyncMap[$r->response] = $r;
248
- $wrappedResponses[] = $r->response;
249
- }
250
- }
251
- }
252
-
253
- if (!$client || !$wrappedResponses) {
254
- return;
255
- }
256
-
257
- foreach ($client->stream($wrappedResponses, $timeout) as $response => $chunk) {
258
- $r = $asyncMap[$response];
259
-
260
- if (null === $chunk->getError()) {
261
- if ($chunk->isFirst()) {
262
- // Ensure no exception is thrown on destruct for the wrapped response
263
- $r->response->getStatusCode();
264
- } elseif (0 === $r->offset && null === $r->content && $chunk->isLast()) {
265
- $r->content = fopen('php://memory', 'w+');
266
- }
267
- }
268
-
269
- if (!$r->passthru) {
270
- if (null !== $chunk->getError() || $chunk->isLast()) {
271
- unset($asyncMap[$response]);
272
- } elseif (null !== $r->content && '' !== ($content = $chunk->getContent()) && \strlen($content) !== fwrite($r->content, $content)) {
273
- $chunk = new ErrorChunk($r->offset, new TransportException(sprintf('Failed writing %d bytes to the response buffer.', \strlen($content))));
274
- $r->info['error'] = $chunk->getError();
275
- $r->response->cancel();
276
- }
277
-
278
- yield $r => $chunk;
279
- continue;
280
- }
281
-
282
- if (null !== $chunk->getError()) {
283
- // no-op
284
- } elseif ($chunk->isFirst()) {
285
- $r->yieldedState = self::FIRST_CHUNK_YIELDED;
286
- } elseif (self::FIRST_CHUNK_YIELDED !== $r->yieldedState && null === $chunk->getInformationalStatus()) {
287
- throw new \LogicException(sprintf('Instance of "%s" is already consumed and cannot be managed by "%s". A decorated client should not call any of the response\'s methods in its "request()" method.', get_debug_type($response), $class ?? static::class));
288
- }
289
-
290
- foreach (self::passthru($r->client, $r, $chunk, $asyncMap) as $chunk) {
291
- yield $r => $chunk;
292
- }
293
-
294
- if ($r->response !== $response && isset($asyncMap[$response])) {
295
- break;
296
- }
297
- }
298
-
299
- if (null === $chunk->getError() && $chunk->isLast()) {
300
- $r->yieldedState = self::LAST_CHUNK_YIELDED;
301
- }
302
- if (null === $chunk->getError() && self::LAST_CHUNK_YIELDED !== $r->yieldedState && $r->response === $response && null !== $r->client) {
303
- throw new \LogicException('A chunk passthru must yield an "isLast()" chunk before ending a stream.');
304
- }
305
-
306
- $responses = [];
307
- foreach ($asyncMap as $response) {
308
- $r = $asyncMap[$response];
309
-
310
- if (null !== $r->client) {
311
- $responses[] = $asyncMap[$response];
312
- }
313
- }
314
- }
315
- }
316
-
317
- /**
318
- * @param \SplObjectStorage<ResponseInterface, AsyncResponse>|null $asyncMap
319
- */
320
- private static function passthru(HttpClientInterface $client, self $r, ChunkInterface $chunk, \SplObjectStorage $asyncMap = null): \Generator
321
- {
322
- $r->stream = null;
323
- $response = $r->response;
324
- $context = new AsyncContext($r->passthru, $client, $r->response, $r->info, $r->content, $r->offset);
325
- if (null === $stream = ($r->passthru)($chunk, $context)) {
326
- if ($r->response === $response && (null !== $chunk->getError() || $chunk->isLast())) {
327
- throw new \LogicException('A chunk passthru cannot swallow the last chunk.');
328
- }
329
-
330
- return;
331
- }
332
-
333
- if (!$stream instanceof \Iterator) {
334
- throw new \LogicException(sprintf('A chunk passthru must return an "Iterator", "%s" returned.', get_debug_type($stream)));
335
- }
336
- $r->stream = $stream;
337
-
338
- yield from self::passthruStream($response, $r, null, $asyncMap);
339
- }
340
-
341
- /**
342
- * @param \SplObjectStorage<ResponseInterface, AsyncResponse>|null $asyncMap
343
- */
344
- private static function passthruStream(ResponseInterface $response, self $r, ?ChunkInterface $chunk, ?\SplObjectStorage $asyncMap): \Generator
345
- {
346
- while (true) {
347
- try {
348
- if (null !== $chunk && $r->stream) {
349
- $r->stream->next();
350
- }
351
-
352
- if (!$r->stream || !$r->stream->valid() || !$r->stream) {
353
- $r->stream = null;
354
- break;
355
- }
356
- } catch (\Throwable $e) {
357
- unset($asyncMap[$response]);
358
- $r->stream = null;
359
- $r->info['error'] = $e->getMessage();
360
- $r->response->cancel();
361
-
362
- yield $r => $chunk = new ErrorChunk($r->offset, $e);
363
- $chunk->didThrow() ?: $chunk->getContent();
364
- break;
365
- }
366
-
367
- $chunk = $r->stream->current();
368
-
369
- if (!$chunk instanceof ChunkInterface) {
370
- throw new \LogicException(sprintf('A chunk passthru must yield instances of "%s", "%s" yielded.', ChunkInterface::class, get_debug_type($chunk)));
371
- }
372
-
373
- if (null !== $chunk->getError()) {
374
- // no-op
375
- } elseif ($chunk->isFirst()) {
376
- $e = $r->openBuffer();
377
-
378
- yield $r => $chunk;
379
-
380
- if ($r->initializer && null === $r->getInfo('error')) {
381
- // Ensure the HTTP status code is always checked
382
- $r->getHeaders(true);
383
- }
384
-
385
- if (null === $e) {
386
- continue;
387
- }
388
-
389
- $r->response->cancel();
390
- $chunk = new ErrorChunk($r->offset, $e);
391
- } elseif ('' !== $content = $chunk->getContent()) {
392
- if (null !== $r->shouldBuffer) {
393
- throw new \LogicException('A chunk passthru must yield an "isFirst()" chunk before any content chunk.');
394
- }
395
-
396
- if (null !== $r->content && \strlen($content) !== fwrite($r->content, $content)) {
397
- $chunk = new ErrorChunk($r->offset, new TransportException(sprintf('Failed writing %d bytes to the response buffer.', \strlen($content))));
398
- $r->info['error'] = $chunk->getError();
399
- $r->response->cancel();
400
- }
401
- }
402
-
403
- if (null !== $chunk->getError() || $chunk->isLast()) {
404
- $stream = $r->stream;
405
- $r->stream = null;
406
- unset($asyncMap[$response]);
407
- }
408
-
409
- if (null === $chunk->getError()) {
410
- $r->offset += \strlen($content);
411
-
412
- yield $r => $chunk;
413
-
414
- if (!$chunk->isLast()) {
415
- continue;
416
- }
417
-
418
- $stream->next();
419
-
420
- if ($stream->valid()) {
421
- throw new \LogicException('A chunk passthru cannot yield after an "isLast()" chunk.');
422
- }
423
-
424
- $r->passthru = null;
425
- } else {
426
- if ($chunk instanceof ErrorChunk) {
427
- $chunk->didThrow(false);
428
- } else {
429
- try {
430
- $chunk = new ErrorChunk($chunk->getOffset(), !$chunk->isTimeout() ?: $chunk->getError());
431
- } catch (TransportExceptionInterface $e) {
432
- $chunk = new ErrorChunk($chunk->getOffset(), $e);
433
- }
434
- }
435
-
436
- yield $r => $chunk;
437
- $chunk->didThrow() ?: $chunk->getContent();
438
- }
439
-
440
- break;
441
- }
442
- }
443
-
444
- private function openBuffer(): ?\Throwable
445
- {
446
- if (null === $shouldBuffer = $this->shouldBuffer) {
447
- throw new \LogicException('A chunk passthru cannot yield more than one "isFirst()" chunk.');
448
- }
449
-
450
- $e = $this->shouldBuffer = null;
451
-
452
- if ($shouldBuffer instanceof \Closure) {
453
- try {
454
- $shouldBuffer = $shouldBuffer($this->getHeaders(false));
455
-
456
- if (null !== $e = $this->response->getInfo('error')) {
457
- throw new TransportException($e);
458
- }
459
- } catch (\Throwable $e) {
460
- $this->info['error'] = $e->getMessage();
461
- $this->response->cancel();
462
- }
463
- }
464
-
465
- if (true === $shouldBuffer) {
466
- $this->content = fopen('php://temp', 'w+');
467
- } elseif (\is_resource($shouldBuffer)) {
468
- $this->content = $shouldBuffer;
469
- }
470
-
471
- return $e;
472
- }
473
-
474
- private function close(): void
475
- {
476
- $this->response->cancel();
477
- }
478
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/CommonResponseTrait.php DELETED
@@ -1,185 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Symfony\Component\HttpClient\Exception\ClientException;
15
- use Symfony\Component\HttpClient\Exception\JsonException;
16
- use Symfony\Component\HttpClient\Exception\RedirectionException;
17
- use Symfony\Component\HttpClient\Exception\ServerException;
18
- use Symfony\Component\HttpClient\Exception\TransportException;
19
-
20
- /**
21
- * Implements common logic for response classes.
22
- *
23
- * @author Nicolas Grekas <p@tchwork.com>
24
- *
25
- * @internal
26
- */
27
- trait CommonResponseTrait
28
- {
29
- /**
30
- * @var callable|null A callback that tells whether we're waiting for response headers
31
- */
32
- private $initializer;
33
- private $shouldBuffer;
34
- private $content;
35
- private $offset = 0;
36
- private $jsonData;
37
-
38
- /**
39
- * {@inheritdoc}
40
- */
41
- public function getContent(bool $throw = true): string
42
- {
43
- if ($this->initializer) {
44
- self::initialize($this);
45
- }
46
-
47
- if ($throw) {
48
- $this->checkStatusCode();
49
- }
50
-
51
- if (null === $this->content) {
52
- $content = null;
53
-
54
- foreach (self::stream([$this]) as $chunk) {
55
- if (!$chunk->isLast()) {
56
- $content .= $chunk->getContent();
57
- }
58
- }
59
-
60
- if (null !== $content) {
61
- return $content;
62
- }
63
-
64
- if (null === $this->content) {
65
- throw new TransportException('Cannot get the content of the response twice: buffering is disabled.');
66
- }
67
- } else {
68
- foreach (self::stream([$this]) as $chunk) {
69
- // Chunks are buffered in $this->content already
70
- }
71
- }
72
-
73
- rewind($this->content);
74
-
75
- return stream_get_contents($this->content);
76
- }
77
-
78
- /**
79
- * {@inheritdoc}
80
- */
81
- public function toArray(bool $throw = true): array
82
- {
83
- if ('' === $content = $this->getContent($throw)) {
84
- throw new JsonException('Response body is empty.');
85
- }
86
-
87
- if (null !== $this->jsonData) {
88
- return $this->jsonData;
89
- }
90
-
91
- try {
92
- $content = json_decode($content, true, 512, \JSON_BIGINT_AS_STRING | (\PHP_VERSION_ID >= 70300 ? \JSON_THROW_ON_ERROR : 0));
93
- } catch (\JsonException $e) {
94
- throw new JsonException($e->getMessage().sprintf(' for "%s".', $this->getInfo('url')), $e->getCode());
95
- }
96
-
97
- if (\PHP_VERSION_ID < 70300 && \JSON_ERROR_NONE !== json_last_error()) {
98
- throw new JsonException(json_last_error_msg().sprintf(' for "%s".', $this->getInfo('url')), json_last_error());
99
- }
100
-
101
- if (!\is_array($content)) {
102
- throw new JsonException(sprintf('JSON content was expected to decode to an array, "%s" returned for "%s".', get_debug_type($content), $this->getInfo('url')));
103
- }
104
-
105
- if (null !== $this->content) {
106
- // Option "buffer" is true
107
- return $this->jsonData = $content;
108
- }
109
-
110
- return $content;
111
- }
112
-
113
- /**
114
- * {@inheritdoc}
115
- */
116
- public function toStream(bool $throw = true)
117
- {
118
- if ($throw) {
119
- // Ensure headers arrived
120
- $this->getHeaders($throw);
121
- }
122
-
123
- $stream = StreamWrapper::createResource($this);
124
- stream_get_meta_data($stream)['wrapper_data']
125
- ->bindHandles($this->handle, $this->content);
126
-
127
- return $stream;
128
- }
129
-
130
- public function __sleep(): array
131
- {
132
- throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
133
- }
134
-
135
- public function __wakeup()
136
- {
137
- throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
138
- }
139
-
140
- /**
141
- * Closes the response and all its network handles.
142
- */
143
- abstract protected function close(): void;
144
-
145
- private static function initialize(self $response): void
146
- {
147
- if (null !== $response->getInfo('error')) {
148
- throw new TransportException($response->getInfo('error'));
149
- }
150
-
151
- try {
152
- if (($response->initializer)($response, -0.0)) {
153
- foreach (self::stream([$response], -0.0) as $chunk) {
154
- if ($chunk->isFirst()) {
155
- break;
156
- }
157
- }
158
- }
159
- } catch (\Throwable $e) {
160
- // Persist timeouts thrown during initialization
161
- $response->info['error'] = $e->getMessage();
162
- $response->close();
163
- throw $e;
164
- }
165
-
166
- $response->initializer = null;
167
- }
168
-
169
- private function checkStatusCode()
170
- {
171
- $code = $this->getInfo('http_code');
172
-
173
- if (500 <= $code) {
174
- throw new ServerException($this);
175
- }
176
-
177
- if (400 <= $code) {
178
- throw new ClientException($this);
179
- }
180
-
181
- if (300 <= $code) {
182
- throw new RedirectionException($this);
183
- }
184
- }
185
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/CurlResponse.php DELETED
@@ -1,474 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Psr\Log\LoggerInterface;
15
- use Symfony\Component\HttpClient\Chunk\FirstChunk;
16
- use Symfony\Component\HttpClient\Chunk\InformationalChunk;
17
- use Symfony\Component\HttpClient\Exception\TransportException;
18
- use Symfony\Component\HttpClient\Internal\Canary;
19
- use Symfony\Component\HttpClient\Internal\ClientState;
20
- use Symfony\Component\HttpClient\Internal\CurlClientState;
21
- use Symfony\Contracts\HttpClient\ResponseInterface;
22
-
23
- /**
24
- * @author Nicolas Grekas <p@tchwork.com>
25
- *
26
- * @internal
27
- */
28
- final class CurlResponse implements ResponseInterface, StreamableInterface
29
- {
30
- use CommonResponseTrait {
31
- getContent as private doGetContent;
32
- }
33
- use TransportResponseTrait;
34
-
35
- private static $performing = false;
36
- private $multi;
37
- private $debugBuffer;
38
-
39
- /**
40
- * @param \CurlHandle|resource|string $ch
41
- *
42
- * @internal
43
- */
44
- public function __construct(CurlClientState $multi, $ch, array $options = null, LoggerInterface $logger = null, string $method = 'GET', callable $resolveRedirect = null, int $curlVersion = null)
45
- {
46
- $this->multi = $multi;
47
-
48
- if (\is_resource($ch) || $ch instanceof \CurlHandle) {
49
- $this->handle = $ch;
50
- $this->debugBuffer = fopen('php://temp', 'w+');
51
- if (0x074000 === $curlVersion) {
52
- fwrite($this->debugBuffer, 'Due to a bug in curl 7.64.0, the debug log is disabled; use another version to work around the issue.');
53
- } else {
54
- curl_setopt($ch, \CURLOPT_VERBOSE, true);
55
- curl_setopt($ch, \CURLOPT_STDERR, $this->debugBuffer);
56
- }
57
- } else {
58
- $this->info['url'] = $ch;
59
- $ch = $this->handle;
60
- }
61
-
62
- $this->id = $id = (int) $ch;
63
- $this->logger = $logger;
64
- $this->shouldBuffer = $options['buffer'] ?? true;
65
- $this->timeout = $options['timeout'] ?? null;
66
- $this->info['http_method'] = $method;
67
- $this->info['user_data'] = $options['user_data'] ?? null;
68
- $this->info['max_duration'] = $options['max_duration'] ?? null;
69
- $this->info['start_time'] = $this->info['start_time'] ?? microtime(true);
70
- $info = &$this->info;
71
- $headers = &$this->headers;
72
- $debugBuffer = $this->debugBuffer;
73
-
74
- if (!$info['response_headers']) {
75
- // Used to keep track of what we're waiting for
76
- curl_setopt($ch, \CURLOPT_PRIVATE, \in_array($method, ['GET', 'HEAD', 'OPTIONS', 'TRACE'], true) && 1.0 < (float) ($options['http_version'] ?? 1.1) ? 'H2' : 'H0'); // H = headers + retry counter
77
- }
78
-
79
- curl_setopt($ch, \CURLOPT_HEADERFUNCTION, static function ($ch, string $data) use (&$info, &$headers, $options, $multi, $id, &$location, $resolveRedirect, $logger): int {
80
- if (0 !== substr_compare($data, "\r\n", -2)) {
81
- return 0;
82
- }
83
-
84
- $len = 0;
85
-
86
- foreach (explode("\r\n", substr($data, 0, -2)) as $data) {
87
- $len += 2 + self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger);
88
- }
89
-
90
- return $len;
91
- });
92
-
93
- if (null === $options) {
94
- // Pushed response: buffer until requested
95
- curl_setopt($ch, \CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use ($multi, $id): int {
96
- $multi->handlesActivity[$id][] = $data;
97
- curl_pause($ch, \CURLPAUSE_RECV);
98
-
99
- return \strlen($data);
100
- });
101
-
102
- return;
103
- }
104
-
105
- $execCounter = $multi->execCounter;
106
- $this->info['pause_handler'] = static function (float $duration) use ($ch, $multi, $execCounter) {
107
- if (0 < $duration) {
108
- if ($execCounter === $multi->execCounter) {
109
- $multi->execCounter = !\is_float($execCounter) ? 1 + $execCounter : \PHP_INT_MIN;
110
- curl_multi_remove_handle($multi->handle, $ch);
111
- }
112
-
113
- $lastExpiry = end($multi->pauseExpiries);
114
- $multi->pauseExpiries[(int) $ch] = $duration += microtime(true);
115
- if (false !== $lastExpiry && $lastExpiry > $duration) {
116
- asort($multi->pauseExpiries);
117
- }
118
- curl_pause($ch, \CURLPAUSE_ALL);
119
- } else {
120
- unset($multi->pauseExpiries[(int) $ch]);
121
- curl_pause($ch, \CURLPAUSE_CONT);
122
- curl_multi_add_handle($multi->handle, $ch);
123
- }
124
- };
125
-
126
- $this->inflate = !isset($options['normalized_headers']['accept-encoding']);
127
- curl_pause($ch, \CURLPAUSE_CONT);
128
-
129
- if ($onProgress = $options['on_progress']) {
130
- $url = isset($info['url']) ? ['url' => $info['url']] : [];
131
- curl_setopt($ch, \CURLOPT_NOPROGRESS, false);
132
- curl_setopt($ch, \CURLOPT_PROGRESSFUNCTION, static function ($ch, $dlSize, $dlNow) use ($onProgress, &$info, $url, $multi, $debugBuffer) {
133
- try {
134
- rewind($debugBuffer);
135
- $debug = ['debug' => stream_get_contents($debugBuffer)];
136
- $onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info + $debug);
137
- } catch (\Throwable $e) {
138
- $multi->handlesActivity[(int) $ch][] = null;
139
- $multi->handlesActivity[(int) $ch][] = $e;
140
-
141
- return 1; // Abort the request
142
- }
143
-
144
- return null;
145
- });
146
- }
147
-
148
- curl_setopt($ch, \CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use ($multi, $id): int {
149
- if ('H' === (curl_getinfo($ch, \CURLINFO_PRIVATE)[0] ?? null)) {
150
- $multi->handlesActivity[$id][] = null;
151
- $multi->handlesActivity[$id][] = new TransportException(sprintf('Unsupported protocol for "%s"', curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL)));
152
-
153
- return 0;
154
- }
155
-
156
- curl_setopt($ch, \CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use ($multi, $id): int {
157
- $multi->handlesActivity[$id][] = $data;
158
-
159
- return \strlen($data);
160
- });
161
-
162
- $multi->handlesActivity[$id][] = $data;
163
-
164
- return \strlen($data);
165
- });
166
-
167
- $this->initializer = static function (self $response) {
168
- $waitFor = curl_getinfo($ch = $response->handle, \CURLINFO_PRIVATE);
169
-
170
- return 'H' === $waitFor[0];
171
- };
172
-
173
- // Schedule the request in a non-blocking way
174
- $multi->lastTimeout = null;
175
- $multi->openHandles[$id] = [$ch, $options];
176
- curl_multi_add_handle($multi->handle, $ch);
177
-
178
- $this->canary = new Canary(static function () use ($ch, $multi, $id) {
179
- unset($multi->pauseExpiries[$id], $multi->openHandles[$id], $multi->handlesActivity[$id]);
180
- curl_setopt($ch, \CURLOPT_PRIVATE, '_0');
181
-
182
- if (self::$performing) {
183
- return;
184
- }
185
-
186
- curl_multi_remove_handle($multi->handle, $ch);
187
- curl_setopt_array($ch, [
188
- \CURLOPT_NOPROGRESS => true,
189
- \CURLOPT_PROGRESSFUNCTION => null,
190
- \CURLOPT_HEADERFUNCTION => null,
191
- \CURLOPT_WRITEFUNCTION => null,
192
- \CURLOPT_READFUNCTION => null,
193
- \CURLOPT_INFILE => null,
194
- ]);
195
-
196
- if (!$multi->openHandles) {
197
- // Schedule DNS cache eviction for the next request
198
- $multi->dnsCache->evictions = $multi->dnsCache->evictions ?: $multi->dnsCache->removals;
199
- $multi->dnsCache->removals = $multi->dnsCache->hostnames = [];
200
- }
201
- });
202
- }
203
-
204
- /**
205
- * {@inheritdoc}
206
- */
207
- public function getInfo(string $type = null)
208
- {
209
- if (!$info = $this->finalInfo) {
210
- $info = array_merge($this->info, curl_getinfo($this->handle));
211
- $info['url'] = $this->info['url'] ?? $info['url'];
212
- $info['redirect_url'] = $this->info['redirect_url'] ?? null;
213
-
214
- // workaround curl not subtracting the time offset for pushed responses
215
- if (isset($this->info['url']) && $info['start_time'] / 1000 < $info['total_time']) {
216
- $info['total_time'] -= $info['starttransfer_time'] ?: $info['total_time'];
217
- $info['starttransfer_time'] = 0.0;
218
- }
219
-
220
- rewind($this->debugBuffer);
221
- $info['debug'] = stream_get_contents($this->debugBuffer);
222
- $waitFor = curl_getinfo($this->handle, \CURLINFO_PRIVATE);
223
-
224
- if ('H' !== $waitFor[0] && 'C' !== $waitFor[0]) {
225
- curl_setopt($this->handle, \CURLOPT_VERBOSE, false);
226
- rewind($this->debugBuffer);
227
- ftruncate($this->debugBuffer, 0);
228
- $this->finalInfo = $info;
229
- }
230
- }
231
-
232
- return null !== $type ? $info[$type] ?? null : $info;
233
- }
234
-
235
- /**
236
- * {@inheritdoc}
237
- */
238
- public function getContent(bool $throw = true): string
239
- {
240
- $performing = self::$performing;
241
- self::$performing = $performing || '_0' === curl_getinfo($this->handle, \CURLINFO_PRIVATE);
242
-
243
- try {
244
- return $this->doGetContent($throw);
245
- } finally {
246
- self::$performing = $performing;
247
- }
248
- }
249
-
250
- public function __destruct()
251
- {
252
- try {
253
- if (null === $this->timeout) {
254
- return; // Unused pushed response
255
- }
256
-
257
- $this->doDestruct();
258
- } finally {
259
- if (\is_resource($this->handle) || $this->handle instanceof \CurlHandle) {
260
- curl_setopt($this->handle, \CURLOPT_VERBOSE, false);
261
- }
262
- }
263
- }
264
-
265
- /**
266
- * {@inheritdoc}
267
- */
268
- private static function schedule(self $response, array &$runningResponses): void
269
- {
270
- if (isset($runningResponses[$i = (int) $response->multi->handle])) {
271
- $runningResponses[$i][1][$response->id] = $response;
272
- } else {
273
- $runningResponses[$i] = [$response->multi, [$response->id => $response]];
274
- }
275
-
276
- if ('_0' === curl_getinfo($ch = $response->handle, \CURLINFO_PRIVATE)) {
277
- // Response already completed
278
- $response->multi->handlesActivity[$response->id][] = null;
279
- $response->multi->handlesActivity[$response->id][] = null !== $response->info['error'] ? new TransportException($response->info['error']) : null;
280
- }
281
- }
282
-
283
- /**
284
- * {@inheritdoc}
285
- *
286
- * @param CurlClientState $multi
287
- */
288
- private static function perform(ClientState $multi, array &$responses = null): void
289
- {
290
- if (self::$performing) {
291
- if ($responses) {
292
- $response = current($responses);
293
- $multi->handlesActivity[(int) $response->handle][] = null;
294
- $multi->handlesActivity[(int) $response->handle][] = new TransportException(sprintf('Userland callback cannot use the client nor the response while processing "%s".', curl_getinfo($response->handle, \CURLINFO_EFFECTIVE_URL)));
295
- }
296
-
297
- return;
298
- }
299
-
300
- try {
301
- self::$performing = true;
302
- ++$multi->execCounter;
303
- $active = 0;
304
- while (\CURLM_CALL_MULTI_PERFORM === ($err = curl_multi_exec($multi->handle, $active))) {
305
- }
306
-
307
- if (\CURLM_OK !== $err) {
308
- throw new TransportException(curl_multi_strerror($err));
309
- }
310
-
311
- while ($info = curl_multi_info_read($multi->handle)) {
312
- if (\CURLMSG_DONE !== $info['msg']) {
313
- continue;
314
- }
315
- $result = $info['result'];
316
- $id = (int) $ch = $info['handle'];
317
- $waitFor = @curl_getinfo($ch, \CURLINFO_PRIVATE) ?: '_0';
318
-
319
- if (\in_array($result, [\CURLE_SEND_ERROR, \CURLE_RECV_ERROR, /*CURLE_HTTP2*/ 16, /*CURLE_HTTP2_STREAM*/ 92], true) && $waitFor[1] && 'C' !== $waitFor[0]) {
320
- curl_multi_remove_handle($multi->handle, $ch);
321
- $waitFor[1] = (string) ((int) $waitFor[1] - 1); // decrement the retry counter
322
- curl_setopt($ch, \CURLOPT_PRIVATE, $waitFor);
323
- curl_setopt($ch, \CURLOPT_FORBID_REUSE, true);
324
-
325
- if (0 === curl_multi_add_handle($multi->handle, $ch)) {
326
- continue;
327
- }
328
- }
329
-
330
- if (\CURLE_RECV_ERROR === $result && 'H' === $waitFor[0] && 400 <= ($responses[(int) $ch]->info['http_code'] ?? 0)) {
331
- $multi->handlesActivity[$id][] = new FirstChunk();
332
- }
333
-
334
- $multi->handlesActivity[$id][] = null;
335
- $multi->handlesActivity[$id][] = \in_array($result, [\CURLE_OK, \CURLE_TOO_MANY_REDIRECTS], true) || '_0' === $waitFor || curl_getinfo($ch, \CURLINFO_SIZE_DOWNLOAD) === curl_getinfo($ch, \CURLINFO_CONTENT_LENGTH_DOWNLOAD) ? null : new TransportException(ucfirst(curl_error($ch) ?: curl_strerror($result)).sprintf(' for "%s".', curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL)));
336
- }
337
- } finally {
338
- self::$performing = false;
339
- }
340
- }
341
-
342
- /**
343
- * {@inheritdoc}
344
- *
345
- * @param CurlClientState $multi
346
- */
347
- private static function select(ClientState $multi, float $timeout): int
348
- {
349
- if (\PHP_VERSION_ID < 70211) {
350
- // workaround https://bugs.php.net/76480
351
- $timeout = min($timeout, 0.01);
352
- }
353
-
354
- if ($multi->pauseExpiries) {
355
- $now = microtime(true);
356
-
357
- foreach ($multi->pauseExpiries as $id => $pauseExpiry) {
358
- if ($now < $pauseExpiry) {
359
- $timeout = min($timeout, $pauseExpiry - $now);
360
- break;
361
- }
362
-
363
- unset($multi->pauseExpiries[$id]);
364
- curl_pause($multi->openHandles[$id][0], \CURLPAUSE_CONT);
365
- curl_multi_add_handle($multi->handle, $multi->openHandles[$id][0]);
366
- }
367
- }
368
-
369
- if (0 !== $selected = curl_multi_select($multi->handle, $timeout)) {
370
- return $selected;
371
- }
372
-
373
- if ($multi->pauseExpiries && 0 < $timeout -= microtime(true) - $now) {
374
- usleep((int) (1E6 * $timeout));
375
- }
376
-
377
- return 0;
378
- }
379
-
380
- /**
381
- * Parses header lines as curl yields them to us.
382
- */
383
- private static function parseHeaderLine($ch, string $data, array &$info, array &$headers, ?array $options, CurlClientState $multi, int $id, ?string &$location, ?callable $resolveRedirect, ?LoggerInterface $logger): int
384
- {
385
- $waitFor = @curl_getinfo($ch, \CURLINFO_PRIVATE) ?: '_0';
386
-
387
- if ('H' !== $waitFor[0]) {
388
- return \strlen($data); // Ignore HTTP trailers
389
- }
390
-
391
- if ('' !== $data) {
392
- // Regular header line: add it to the list
393
- self::addResponseHeaders([$data], $info, $headers);
394
-
395
- if (!str_starts_with($data, 'HTTP/')) {
396
- if (0 === stripos($data, 'Location:')) {
397
- $location = trim(substr($data, 9));
398
- }
399
-
400
- return \strlen($data);
401
- }
402
-
403
- if (\function_exists('openssl_x509_read') && $certinfo = curl_getinfo($ch, \CURLINFO_CERTINFO)) {
404
- $info['peer_certificate_chain'] = array_map('openssl_x509_read', array_column($certinfo, 'Cert'));
405
- }
406
-
407
- if (300 <= $info['http_code'] && $info['http_code'] < 400) {
408
- if (curl_getinfo($ch, \CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) {
409
- curl_setopt($ch, \CURLOPT_FOLLOWLOCATION, false);
410
- } elseif (303 === $info['http_code'] || ('POST' === $info['http_method'] && \in_array($info['http_code'], [301, 302], true))) {
411
- curl_setopt($ch, \CURLOPT_POSTFIELDS, '');
412
- }
413
- }
414
-
415
- return \strlen($data);
416
- }
417
-
418
- // End of headers: handle informational responses, redirects, etc.
419
-
420
- if (200 > $statusCode = curl_getinfo($ch, \CURLINFO_RESPONSE_CODE)) {
421
- $multi->handlesActivity[$id][] = new InformationalChunk($statusCode, $headers);
422
- $location = null;
423
-
424
- return \strlen($data);
425
- }
426
-
427
- $info['redirect_url'] = null;
428
-
429
- if (300 <= $statusCode && $statusCode < 400 && null !== $location) {
430
- if ($noContent = 303 === $statusCode || ('POST' === $info['http_method'] && \in_array($statusCode, [301, 302], true))) {
431
- $info['http_method'] = 'HEAD' === $info['http_method'] ? 'HEAD' : 'GET';
432
- curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, $info['http_method']);
433
- }
434
-
435
- if (null === $info['redirect_url'] = $resolveRedirect($ch, $location, $noContent)) {
436
- $options['max_redirects'] = curl_getinfo($ch, \CURLINFO_REDIRECT_COUNT);
437
- curl_setopt($ch, \CURLOPT_FOLLOWLOCATION, false);
438
- curl_setopt($ch, \CURLOPT_MAXREDIRS, $options['max_redirects']);
439
- } else {
440
- $url = parse_url($location ?? ':');
441
-
442
- if (isset($url['host']) && null !== $ip = $multi->dnsCache->hostnames[$url['host'] = strtolower($url['host'])] ?? null) {
443
- // Populate DNS cache for redirects if needed
444
- $port = $url['port'] ?? ('http' === ($url['scheme'] ?? parse_url(curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL), \PHP_URL_SCHEME)) ? 80 : 443);
445
- curl_setopt($ch, \CURLOPT_RESOLVE, ["{$url['host']}:$port:$ip"]);
446
- $multi->dnsCache->removals["-{$url['host']}:$port"] = "-{$url['host']}:$port";
447
- }
448
- }
449
- }
450
-
451
- if (401 === $statusCode && isset($options['auth_ntlm']) && 0 === strncasecmp($headers['www-authenticate'][0] ?? '', 'NTLM ', 5)) {
452
- // Continue with NTLM auth
453
- } elseif ($statusCode < 300 || 400 <= $statusCode || null === $location || curl_getinfo($ch, \CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) {
454
- // Headers and redirects completed, time to get the response's content
455
- $multi->handlesActivity[$id][] = new FirstChunk();
456
-
457
- if ('HEAD' === $info['http_method'] || \in_array($statusCode, [204, 304], true)) {
458
- $waitFor = '_0'; // no content expected
459
- $multi->handlesActivity[$id][] = null;
460
- $multi->handlesActivity[$id][] = null;
461
- } else {
462
- $waitFor[0] = 'C'; // C = content
463
- }
464
-
465
- curl_setopt($ch, \CURLOPT_PRIVATE, $waitFor);
466
- } elseif (null !== $info['redirect_url'] && $logger) {
467
- $logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url']));
468
- }
469
-
470
- $location = null;
471
-
472
- return \strlen($data);
473
- }
474
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/HttplugPromise.php DELETED
@@ -1,80 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use GuzzleHttp\Promise\Create;
15
- use GuzzleHttp\Promise\PromiseInterface as GuzzlePromiseInterface;
16
- use Http\Promise\Promise as HttplugPromiseInterface;
17
- use Psr\Http\Message\ResponseInterface as Psr7ResponseInterface;
18
-
19
- /**
20
- * @author Tobias Nyholm <tobias.nyholm@gmail.com>
21
- *
22
- * @internal
23
- */
24
- final class HttplugPromise implements HttplugPromiseInterface
25
- {
26
- private $promise;
27
-
28
- public function __construct(GuzzlePromiseInterface $promise)
29
- {
30
- $this->promise = $promise;
31
- }
32
-
33
- public function then(callable $onFulfilled = null, callable $onRejected = null): self
34
- {
35
- return new self($this->promise->then(
36
- $this->wrapThenCallback($onFulfilled),
37
- $this->wrapThenCallback($onRejected)
38
- ));
39
- }
40
-
41
- public function cancel(): void
42
- {
43
- $this->promise->cancel();
44
- }
45
-
46
- /**
47
- * {@inheritdoc}
48
- */
49
- public function getState(): string
50
- {
51
- return $this->promise->getState();
52
- }
53
-
54
- /**
55
- * {@inheritdoc}
56
- *
57
- * @return Psr7ResponseInterface|mixed
58
- */
59
- public function wait($unwrap = true)
60
- {
61
- $result = $this->promise->wait($unwrap);
62
-
63
- while ($result instanceof HttplugPromiseInterface || $result instanceof GuzzlePromiseInterface) {
64
- $result = $result->wait($unwrap);
65
- }
66
-
67
- return $result;
68
- }
69
-
70
- private function wrapThenCallback(?callable $callback): ?callable
71
- {
72
- if (null === $callback) {
73
- return null;
74
- }
75
-
76
- return static function ($value) use ($callback) {
77
- return Create::promiseFor($callback($value));
78
- };
79
- }
80
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/MockResponse.php DELETED
@@ -1,339 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Symfony\Component\HttpClient\Chunk\ErrorChunk;
15
- use Symfony\Component\HttpClient\Chunk\FirstChunk;
16
- use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
17
- use Symfony\Component\HttpClient\Exception\TransportException;
18
- use Symfony\Component\HttpClient\Internal\ClientState;
19
- use Symfony\Contracts\HttpClient\ResponseInterface;
20
-
21
- /**
22
- * A test-friendly response.
23
- *
24
- * @author Nicolas Grekas <p@tchwork.com>
25
- */
26
- class MockResponse implements ResponseInterface, StreamableInterface
27
- {
28
- use CommonResponseTrait;
29
- use TransportResponseTrait {
30
- doDestruct as public __destruct;
31
- }
32
-
33
- private $body;
34
- private $requestOptions = [];
35
- private $requestUrl;
36
- private $requestMethod;
37
-
38
- private static $mainMulti;
39
- private static $idSequence = 0;
40
-
41
- /**
42
- * @param string|string[]|iterable $body The response body as a string or an iterable of strings,
43
- * yielding an empty string simulates an idle timeout,
44
- * throwing an exception yields an ErrorChunk
45
- *
46
- * @see ResponseInterface::getInfo() for possible info, e.g. "response_headers"
47
- */
48
- public function __construct($body = '', array $info = [])
49
- {
50
- $this->body = is_iterable($body) ? $body : (string) $body;
51
- $this->info = $info + ['http_code' => 200] + $this->info;
52
-
53
- if (!isset($info['response_headers'])) {
54
- return;
55
- }
56
-
57
- $responseHeaders = [];
58
-
59
- foreach ($info['response_headers'] as $k => $v) {
60
- foreach ((array) $v as $v) {
61
- $responseHeaders[] = (\is_string($k) ? $k.': ' : '').$v;
62
- }
63
- }
64
-
65
- $this->info['response_headers'] = [];
66
- self::addResponseHeaders($responseHeaders, $this->info, $this->headers);
67
- }
68
-
69
- /**
70
- * Returns the options used when doing the request.
71
- */
72
- public function getRequestOptions(): array
73
- {
74
- return $this->requestOptions;
75
- }
76
-
77
- /**
78
- * Returns the URL used when doing the request.
79
- */
80
- public function getRequestUrl(): string
81
- {
82
- return $this->requestUrl;
83
- }
84
-
85
- /**
86
- * Returns the method used when doing the request.
87
- */
88
- public function getRequestMethod(): string
89
- {
90
- return $this->requestMethod;
91
- }
92
-
93
- /**
94
- * {@inheritdoc}
95
- */
96
- public function getInfo(string $type = null)
97
- {
98
- return null !== $type ? $this->info[$type] ?? null : $this->info;
99
- }
100
-
101
- /**
102
- * {@inheritdoc}
103
- */
104
- public function cancel(): void
105
- {
106
- $this->info['canceled'] = true;
107
- $this->info['error'] = 'Response has been canceled.';
108
- try {
109
- $this->body = null;
110
- } catch (TransportException $e) {
111
- // ignore errors when canceling
112
- }
113
- }
114
-
115
- /**
116
- * {@inheritdoc}
117
- */
118
- protected function close(): void
119
- {
120
- $this->inflate = null;
121
- $this->body = [];
122
- }
123
-
124
- /**
125
- * @internal
126
- */
127
- public static function fromRequest(string $method, string $url, array $options, ResponseInterface $mock): self
128
- {
129
- $response = new self([]);
130
- $response->requestOptions = $options;
131
- $response->id = ++self::$idSequence;
132
- $response->shouldBuffer = $options['buffer'] ?? true;
133
- $response->initializer = static function (self $response) {
134
- return \is_array($response->body[0] ?? null);
135
- };
136
-
137
- $response->info['redirect_count'] = 0;
138
- $response->info['redirect_url'] = null;
139
- $response->info['start_time'] = microtime(true);
140
- $response->info['http_method'] = $method;
141
- $response->info['http_code'] = 0;
142
- $response->info['user_data'] = $options['user_data'] ?? null;
143
- $response->info['max_duration'] = $options['max_duration'] ?? null;
144
- $response->info['url'] = $url;
145
-
146
- if ($mock instanceof self) {
147
- $mock->requestOptions = $response->requestOptions;
148
- $mock->requestMethod = $method;
149
- $mock->requestUrl = $url;
150
- }
151
-
152
- self::writeRequest($response, $options, $mock);
153
- $response->body[] = [$options, $mock];
154
-
155
- return $response;
156
- }
157
-
158
- /**
159
- * {@inheritdoc}
160
- */
161
- protected static function schedule(self $response, array &$runningResponses): void
162
- {
163
- if (!$response->id) {
164
- throw new InvalidArgumentException('MockResponse instances must be issued by MockHttpClient before processing.');
165
- }
166
-
167
- $multi = self::$mainMulti ?? self::$mainMulti = new ClientState();
168
-
169
- if (!isset($runningResponses[0])) {
170
- $runningResponses[0] = [$multi, []];
171
- }
172
-
173
- $runningResponses[0][1][$response->id] = $response;
174
- }
175
-
176
- /**
177
- * {@inheritdoc}
178
- */
179
- protected static function perform(ClientState $multi, array &$responses): void
180
- {
181
- foreach ($responses as $response) {
182
- $id = $response->id;
183
-
184
- if (null === $response->body) {
185
- // Canceled response
186
- $response->body = [];
187
- } elseif ([] === $response->body) {
188
- // Error chunk
189
- $multi->handlesActivity[$id][] = null;
190
- $multi->handlesActivity[$id][] = null !== $response->info['error'] ? new TransportException($response->info['error']) : null;
191
- } elseif (null === $chunk = array_shift($response->body)) {
192
- // Last chunk
193
- $multi->handlesActivity[$id][] = null;
194
- $multi->handlesActivity[$id][] = array_shift($response->body);
195
- } elseif (\is_array($chunk)) {
196
- // First chunk
197
- try {
198
- $offset = 0;
199
- $chunk[1]->getStatusCode();
200
- $chunk[1]->getHeaders(false);
201
- self::readResponse($response, $chunk[0], $chunk[1], $offset);
202
- $multi->handlesActivity[$id][] = new FirstChunk();
203
- $buffer = $response->requestOptions['buffer'] ?? null;
204
-
205
- if ($buffer instanceof \Closure && $response->content = $buffer($response->headers) ?: null) {
206
- $response->content = \is_resource($response->content) ? $response->content : fopen('php://temp', 'w+');
207
- }
208
- } catch (\Throwable $e) {
209
- $multi->handlesActivity[$id][] = null;
210
- $multi->handlesActivity[$id][] = $e;
211
- }
212
- } elseif ($chunk instanceof \Throwable) {
213
- $multi->handlesActivity[$id][] = null;
214
- $multi->handlesActivity[$id][] = $chunk;
215
- } else {
216
- // Data or timeout chunk
217
- $multi->handlesActivity[$id][] = $chunk;
218
- }
219
- }
220
- }
221
-
222
- /**
223
- * {@inheritdoc}
224
- */
225
- protected static function select(ClientState $multi, float $timeout): int
226
- {
227
- return 42;
228
- }
229
-
230
- /**
231
- * Simulates sending the request.
232
- */
233
- private static function writeRequest(self $response, array $options, ResponseInterface $mock)
234
- {
235
- $onProgress = $options['on_progress'] ?? static function () {};
236
- $response->info += $mock->getInfo() ?: [];
237
-
238
- // simulate "size_upload" if it is set
239
- if (isset($response->info['size_upload'])) {
240
- $response->info['size_upload'] = 0.0;
241
- }
242
-
243
- // simulate "total_time" if it is not set
244
- if (!isset($response->info['total_time'])) {
245
- $response->info['total_time'] = microtime(true) - $response->info['start_time'];
246
- }
247
-
248
- // "notify" DNS resolution
249
- $onProgress(0, 0, $response->info);
250
-
251
- // consume the request body
252
- if (\is_resource($body = $options['body'] ?? '')) {
253
- $data = stream_get_contents($body);
254
- if (isset($response->info['size_upload'])) {
255
- $response->info['size_upload'] += \strlen($data);
256
- }
257
- } elseif ($body instanceof \Closure) {
258
- while ('' !== $data = $body(16372)) {
259
- if (!\is_string($data)) {
260
- throw new TransportException(sprintf('Return value of the "body" option callback must be string, "%s" returned.', get_debug_type($data)));
261
- }
262
-
263
- // "notify" upload progress
264
- if (isset($response->info['size_upload'])) {
265
- $response->info['size_upload'] += \strlen($data);
266
- }
267
-
268
- $onProgress(0, 0, $response->info);
269
- }
270
- }
271
- }
272
-
273
- /**
274
- * Simulates reading the response.
275
- */
276
- private static function readResponse(self $response, array $options, ResponseInterface $mock, int &$offset)
277
- {
278
- $onProgress = $options['on_progress'] ?? static function () {};
279
-
280
- // populate info related to headers
281
- $info = $mock->getInfo() ?: [];
282
- $response->info['http_code'] = ($info['http_code'] ?? 0) ?: $mock->getStatusCode() ?: 200;
283
- $response->addResponseHeaders($info['response_headers'] ?? [], $response->info, $response->headers);
284
- $dlSize = isset($response->headers['content-encoding']) || 'HEAD' === $response->info['http_method'] || \in_array($response->info['http_code'], [204, 304], true) ? 0 : (int) ($response->headers['content-length'][0] ?? 0);
285
-
286
- $response->info = [
287
- 'start_time' => $response->info['start_time'],
288
- 'user_data' => $response->info['user_data'],
289
- 'max_duration' => $response->info['max_duration'],
290
- 'http_code' => $response->info['http_code'],
291
- ] + $info + $response->info;
292
-
293
- if (null !== $response->info['error']) {
294
- throw new TransportException($response->info['error']);
295
- }
296
-
297
- if (!isset($response->info['total_time'])) {
298
- $response->info['total_time'] = microtime(true) - $response->info['start_time'];
299
- }
300
-
301
- // "notify" headers arrival
302
- $onProgress(0, $dlSize, $response->info);
303
-
304
- // cast response body to activity list
305
- $body = $mock instanceof self ? $mock->body : $mock->getContent(false);
306
-
307
- if (!\is_string($body)) {
308
- try {
309
- foreach ($body as $chunk) {
310
- if ('' === $chunk = (string) $chunk) {
311
- // simulate an idle timeout
312
- $response->body[] = new ErrorChunk($offset, sprintf('Idle timeout reached for "%s".', $response->info['url']));
313
- } else {
314
- $response->body[] = $chunk;
315
- $offset += \strlen($chunk);
316
- // "notify" download progress
317
- $onProgress($offset, $dlSize, $response->info);
318
- }
319
- }
320
- } catch (\Throwable $e) {
321
- $response->body[] = $e;
322
- }
323
- } elseif ('' !== $body) {
324
- $response->body[] = $body;
325
- $offset = \strlen($body);
326
- }
327
-
328
- if (!isset($response->info['total_time'])) {
329
- $response->info['total_time'] = microtime(true) - $response->info['start_time'];
330
- }
331
-
332
- // "notify" completion
333
- $onProgress($offset, $dlSize, $response->info);
334
-
335
- if ($dlSize && $offset !== $dlSize) {
336
- throw new TransportException(sprintf('Transfer closed with %d bytes remaining to read.', $dlSize - $offset));
337
- }
338
- }
339
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/NativeResponse.php DELETED
@@ -1,376 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Psr\Log\LoggerInterface;
15
- use Symfony\Component\HttpClient\Chunk\FirstChunk;
16
- use Symfony\Component\HttpClient\Exception\TransportException;
17
- use Symfony\Component\HttpClient\Internal\Canary;
18
- use Symfony\Component\HttpClient\Internal\ClientState;
19
- use Symfony\Component\HttpClient\Internal\NativeClientState;
20
- use Symfony\Contracts\HttpClient\ResponseInterface;
21
-
22
- /**
23
- * @author Nicolas Grekas <p@tchwork.com>
24
- *
25
- * @internal
26
- */
27
- final class NativeResponse implements ResponseInterface, StreamableInterface
28
- {
29
- use CommonResponseTrait;
30
- use TransportResponseTrait;
31
-
32
- private $context;
33
- private $url;
34
- private $resolver;
35
- private $onProgress;
36
- private $remaining;
37
- private $buffer;
38
- private $multi;
39
- private $pauseExpiry = 0;
40
-
41
- /**
42
- * @internal
43
- */
44
- public function __construct(NativeClientState $multi, $context, string $url, array $options, array &$info, callable $resolver, ?callable $onProgress, ?LoggerInterface $logger)
45
- {
46
- $this->multi = $multi;
47
- $this->id = $id = (int) $context;
48
- $this->context = $context;
49
- $this->url = $url;
50
- $this->logger = $logger;
51
- $this->timeout = $options['timeout'];
52
- $this->info = &$info;
53
- $this->resolver = $resolver;
54
- $this->onProgress = $onProgress;
55
- $this->inflate = !isset($options['normalized_headers']['accept-encoding']);
56
- $this->shouldBuffer = $options['buffer'] ?? true;
57
-
58
- // Temporary resource to dechunk the response stream
59
- $this->buffer = fopen('php://temp', 'w+');
60
-
61
- $info['user_data'] = $options['user_data'];
62
- $info['max_duration'] = $options['max_duration'];
63
- ++$multi->responseCount;
64
-
65
- $this->initializer = static function (self $response) {
66
- return null === $response->remaining;
67
- };
68
-
69
- $pauseExpiry = &$this->pauseExpiry;
70
- $info['pause_handler'] = static function (float $duration) use (&$pauseExpiry) {
71
- $pauseExpiry = 0 < $duration ? microtime(true) + $duration : 0;
72
- };
73
-
74
- $this->canary = new Canary(static function () use ($multi, $id) {
75
- if (null !== ($host = $multi->openHandles[$id][6] ?? null) && 0 >= --$multi->hosts[$host]) {
76
- unset($multi->hosts[$host]);
77
- }
78
- unset($multi->openHandles[$id], $multi->handlesActivity[$id]);
79
- });
80
- }
81
-
82
- /**
83
- * {@inheritdoc}
84
- */
85
- public function getInfo(string $type = null)
86
- {
87
- if (!$info = $this->finalInfo) {
88
- $info = $this->info;
89
- $info['url'] = implode('', $info['url']);
90
- unset($info['size_body'], $info['request_header']);
91
-
92
- if (null === $this->buffer) {
93
- $this->finalInfo = $info;
94
- }
95
- }
96
-
97
- return null !== $type ? $info[$type] ?? null : $info;
98
- }
99
-
100
- public function __destruct()
101
- {
102
- try {
103
- $this->doDestruct();
104
- } finally {
105
- // Clear the DNS cache when all requests completed
106
- if (0 >= --$this->multi->responseCount) {
107
- $this->multi->responseCount = 0;
108
- $this->multi->dnsCache = [];
109
- }
110
- }
111
- }
112
-
113
- private function open(): void
114
- {
115
- $url = $this->url;
116
-
117
- set_error_handler(function ($type, $msg) use (&$url) {
118
- if (\E_NOTICE !== $type || 'fopen(): Content-type not specified assuming application/x-www-form-urlencoded' !== $msg) {
119
- throw new TransportException($msg);
120
- }
121
-
122
- $this->logger && $this->logger->info(sprintf('%s for "%s".', $msg, $url ?? $this->url));
123
- });
124
-
125
- try {
126
- $this->info['start_time'] = microtime(true);
127
-
128
- [$resolver, $url] = ($this->resolver)($this->multi);
129
-
130
- while (true) {
131
- $context = stream_context_get_options($this->context);
132
-
133
- if ($proxy = $context['http']['proxy'] ?? null) {
134
- $this->info['debug'] .= "* Establish HTTP proxy tunnel to {$proxy}\n";
135
- $this->info['request_header'] = $url;
136
- } else {
137
- $this->info['debug'] .= "* Trying {$this->info['primary_ip']}...\n";
138
- $this->info['request_header'] = $this->info['url']['path'].$this->info['url']['query'];
139
- }
140
-
141
- $this->info['request_header'] = sprintf("> %s %s HTTP/%s \r\n", $context['http']['method'], $this->info['request_header'], $context['http']['protocol_version']);
142
- $this->info['request_header'] .= implode("\r\n", $context['http']['header'])."\r\n\r\n";
143
-
144
- if (\array_key_exists('peer_name', $context['ssl']) && null === $context['ssl']['peer_name']) {
145
- unset($context['ssl']['peer_name']);
146
- $this->context = stream_context_create([], ['options' => $context] + stream_context_get_params($this->context));
147
- }
148
-
149
- // Send request and follow redirects when needed
150
- $this->handle = $h = fopen($url, 'r', false, $this->context);
151
- self::addResponseHeaders(stream_get_meta_data($h)['wrapper_data'], $this->info, $this->headers, $this->info['debug']);
152
- $url = $resolver($this->multi, $this->headers['location'][0] ?? null, $this->context);
153
-
154
- if (null === $url) {
155
- break;
156
- }
157
-
158
- $this->logger && $this->logger->info(sprintf('Redirecting: "%s %s"', $this->info['http_code'], $url ?? $this->url));
159
- }
160
- } catch (\Throwable $e) {
161
- $this->close();
162
- $this->multi->handlesActivity[$this->id][] = null;
163
- $this->multi->handlesActivity[$this->id][] = $e;
164
-
165
- return;
166
- } finally {
167
- $this->info['pretransfer_time'] = $this->info['total_time'] = microtime(true) - $this->info['start_time'];
168
- restore_error_handler();
169
- }
170
-
171
- if (isset($context['ssl']['capture_peer_cert_chain']) && isset(($context = stream_context_get_options($this->context))['ssl']['peer_certificate_chain'])) {
172
- $this->info['peer_certificate_chain'] = $context['ssl']['peer_certificate_chain'];
173
- }
174
-
175
- stream_set_blocking($h, false);
176
- $this->context = $this->resolver = null;
177
-
178
- // Create dechunk buffers
179
- if (isset($this->headers['content-length'])) {
180
- $this->remaining = (int) $this->headers['content-length'][0];
181
- } elseif ('chunked' === ($this->headers['transfer-encoding'][0] ?? null)) {
182
- stream_filter_append($this->buffer, 'dechunk', \STREAM_FILTER_WRITE);
183
- $this->remaining = -1;
184
- } else {
185
- $this->remaining = -2;
186
- }
187
-
188
- $this->multi->handlesActivity[$this->id] = [new FirstChunk()];
189
-
190
- if ('HEAD' === $context['http']['method'] || \in_array($this->info['http_code'], [204, 304], true)) {
191
- $this->multi->handlesActivity[$this->id][] = null;
192
- $this->multi->handlesActivity[$this->id][] = null;
193
-
194
- return;
195
- }
196
-
197
- $host = parse_url($this->info['redirect_url'] ?? $this->url, \PHP_URL_HOST);
198
- $this->multi->lastTimeout = null;
199
- $this->multi->openHandles[$this->id] = [&$this->pauseExpiry, $h, $this->buffer, $this->onProgress, &$this->remaining, &$this->info, $host];
200
- $this->multi->hosts[$host] = 1 + ($this->multi->hosts[$host] ?? 0);
201
- }
202
-
203
- /**
204
- * {@inheritdoc}
205
- */
206
- private function close(): void
207
- {
208
- $this->canary->cancel();
209
- $this->handle = $this->buffer = $this->inflate = $this->onProgress = null;
210
- }
211
-
212
- /**
213
- * {@inheritdoc}
214
- */
215
- private static function schedule(self $response, array &$runningResponses): void
216
- {
217
- if (!isset($runningResponses[$i = $response->multi->id])) {
218
- $runningResponses[$i] = [$response->multi, []];
219
- }
220
-
221
- $runningResponses[$i][1][$response->id] = $response;
222
-
223
- if (null === $response->buffer) {
224
- // Response already completed
225
- $response->multi->handlesActivity[$response->id][] = null;
226
- $response->multi->handlesActivity[$response->id][] = null !== $response->info['error'] ? new TransportException($response->info['error']) : null;
227
- }
228
- }
229
-
230
- /**
231
- * {@inheritdoc}
232
- *
233
- * @param NativeClientState $multi
234
- */
235
- private static function perform(ClientState $multi, array &$responses = null): void
236
- {
237
- foreach ($multi->openHandles as $i => [$pauseExpiry, $h, $buffer, $onProgress]) {
238
- if ($pauseExpiry) {
239
- if (microtime(true) < $pauseExpiry) {
240
- continue;
241
- }
242
-
243
- $multi->openHandles[$i][0] = 0;
244
- }
245
-
246
- $hasActivity = false;
247
- $remaining = &$multi->openHandles[$i][4];
248
- $info = &$multi->openHandles[$i][5];
249
- $e = null;
250
-
251
- // Read incoming buffer and write it to the dechunk one
252
- try {
253
- if ($remaining && '' !== $data = (string) fread($h, 0 > $remaining ? 16372 : $remaining)) {
254
- fwrite($buffer, $data);
255
- $hasActivity = true;
256
- $multi->sleep = false;
257
-
258
- if (-1 !== $remaining) {
259
- $remaining -= \strlen($data);
260
- }
261
- }
262
- } catch (\Throwable $e) {
263
- $hasActivity = $onProgress = false;
264
- }
265
-
266
- if (!$hasActivity) {
267
- if ($onProgress) {
268
- try {
269
- // Notify the progress callback so that it can e.g. cancel
270
- // the request if the stream is inactive for too long
271
- $info['total_time'] = microtime(true) - $info['start_time'];
272
- $onProgress();
273
- } catch (\Throwable $e) {
274
- // no-op
275
- }
276
- }
277
- } elseif ('' !== $data = stream_get_contents($buffer, -1, 0)) {
278
- rewind($buffer);
279
- ftruncate($buffer, 0);
280
-
281
- if (null === $e) {
282
- $multi->handlesActivity[$i][] = $data;
283
- }
284
- }
285
-
286
- if (null !== $e || !$remaining || feof($h)) {
287
- // Stream completed
288
- $info['total_time'] = microtime(true) - $info['start_time'];
289
- $info['starttransfer_time'] = $info['starttransfer_time'] ?: $info['total_time'];
290
-
291
- if ($onProgress) {
292
- try {
293
- $onProgress(-1);
294
- } catch (\Throwable $e) {
295
- // no-op
296
- }
297
- }
298
-
299
- if (null === $e) {
300
- if (0 < $remaining) {
301
- $e = new TransportException(sprintf('Transfer closed with %s bytes remaining to read.', $remaining));
302
- } elseif (-1 === $remaining && fwrite($buffer, '-') && '' !== stream_get_contents($buffer, -1, 0)) {
303
- $e = new TransportException('Transfer closed with outstanding data remaining from chunked response.');
304
- }
305
- }
306
-
307
- $multi->handlesActivity[$i][] = null;
308
- $multi->handlesActivity[$i][] = $e;
309
- if (null !== ($host = $multi->openHandles[$i][6] ?? null) && 0 >= --$multi->hosts[$host]) {
310
- unset($multi->hosts[$host]);
311
- }
312
- unset($multi->openHandles[$i]);
313
- $multi->sleep = false;
314
- }
315
- }
316
-
317
- if (null === $responses) {
318
- return;
319
- }
320
-
321
- $maxHosts = $multi->maxHostConnections;
322
-
323
- foreach ($responses as $i => $response) {
324
- if (null !== $response->remaining || null === $response->buffer) {
325
- continue;
326
- }
327
-
328
- if ($response->pauseExpiry && microtime(true) < $response->pauseExpiry) {
329
- // Create empty open handles to tell we still have pending requests
330
- $multi->openHandles[$i] = [\INF, null, null, null];
331
- } elseif ($maxHosts && $maxHosts > ($multi->hosts[parse_url($response->url, \PHP_URL_HOST)] ?? 0)) {
332
- // Open the next pending request - this is a blocking operation so we do only one of them
333
- $response->open();
334
- $multi->sleep = false;
335
- self::perform($multi);
336
- $maxHosts = 0;
337
- }
338
- }
339
- }
340
-
341
- /**
342
- * {@inheritdoc}
343
- *
344
- * @param NativeClientState $multi
345
- */
346
- private static function select(ClientState $multi, float $timeout): int
347
- {
348
- if (!$multi->sleep = !$multi->sleep) {
349
- return -1;
350
- }
351
-
352
- $_ = $handles = [];
353
- $now = null;
354
-
355
- foreach ($multi->openHandles as [$pauseExpiry, $h]) {
356
- if (null === $h) {
357
- continue;
358
- }
359
-
360
- if ($pauseExpiry && ($now ?? $now = microtime(true)) < $pauseExpiry) {
361
- $timeout = min($timeout, $pauseExpiry - $now);
362
- continue;
363
- }
364
-
365
- $handles[] = $h;
366
- }
367
-
368
- if (!$handles) {
369
- usleep((int) (1E6 * $timeout));
370
-
371
- return 0;
372
- }
373
-
374
- return stream_select($handles, $_, $_, (int) $timeout, (int) (1E6 * ($timeout - (int) $timeout)));
375
- }
376
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/ResponseStream.php DELETED
@@ -1,54 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Symfony\Contracts\HttpClient\ChunkInterface;
15
- use Symfony\Contracts\HttpClient\ResponseInterface;
16
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
17
-
18
- /**
19
- * @author Nicolas Grekas <p@tchwork.com>
20
- */
21
- final class ResponseStream implements ResponseStreamInterface
22
- {
23
- private $generator;
24
-
25
- public function __construct(\Generator $generator)
26
- {
27
- $this->generator = $generator;
28
- }
29
-
30
- public function key(): ResponseInterface
31
- {
32
- return $this->generator->key();
33
- }
34
-
35
- public function current(): ChunkInterface
36
- {
37
- return $this->generator->current();
38
- }
39
-
40
- public function next(): void
41
- {
42
- $this->generator->next();
43
- }
44
-
45
- public function rewind(): void
46
- {
47
- $this->generator->rewind();
48
- }
49
-
50
- public function valid(): bool
51
- {
52
- return $this->generator->valid();
53
- }
54
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/StreamWrapper.php DELETED
@@ -1,304 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
15
- use Symfony\Contracts\HttpClient\HttpClientInterface;
16
- use Symfony\Contracts\HttpClient\ResponseInterface;
17
-
18
- /**
19
- * Allows turning ResponseInterface instances to PHP streams.
20
- *
21
- * @author Nicolas Grekas <p@tchwork.com>
22
- */
23
- class StreamWrapper
24
- {
25
- /** @var resource|string|null */
26
- public $context;
27
-
28
- /** @var HttpClientInterface */
29
- private $client;
30
-
31
- /** @var ResponseInterface */
32
- private $response;
33
-
34
- /** @var resource|null */
35
- private $content;
36
-
37
- /** @var resource|null */
38
- private $handle;
39
-
40
- private $blocking = true;
41
- private $timeout;
42
- private $eof = false;
43
- private $offset = 0;
44
-
45
- /**
46
- * Creates a PHP stream resource from a ResponseInterface.
47
- *
48
- * @return resource
49
- */
50
- public static function createResource(ResponseInterface $response, HttpClientInterface $client = null)
51
- {
52
- if ($response instanceof StreamableInterface) {
53
- $stack = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, 2);
54
-
55
- if ($response !== ($stack[1]['object'] ?? null)) {
56
- return $response->toStream(false);
57
- }
58
- }
59
-
60
- if (null === $client && !method_exists($response, 'stream')) {
61
- throw new \InvalidArgumentException(sprintf('Providing a client to "%s()" is required when the response doesn\'t have any "stream()" method.', __CLASS__));
62
- }
63
-
64
- if (false === stream_wrapper_register('symfony', __CLASS__)) {
65
- throw new \RuntimeException(error_get_last()['message'] ?? 'Registering the "symfony" stream wrapper failed.');
66
- }
67
-
68
- try {
69
- $context = [
70
- 'client' => $client ?? $response,
71
- 'response' => $response,
72
- ];
73
-
74
- return fopen('symfony://'.$response->getInfo('url'), 'r', false, stream_context_create(['symfony' => $context])) ?: null;
75
- } finally {
76
- stream_wrapper_unregister('symfony');
77
- }
78
- }
79
-
80
- public function getResponse(): ResponseInterface
81
- {
82
- return $this->response;
83
- }
84
-
85
- /**
86
- * @param resource|callable|null $handle The resource handle that should be monitored when
87
- * stream_select() is used on the created stream
88
- * @param resource|null $content The seekable resource where the response body is buffered
89
- */
90
- public function bindHandles(&$handle, &$content): void
91
- {
92
- $this->handle = &$handle;
93
- $this->content = &$content;
94
- }
95
-
96
- public function stream_open(string $path, string $mode, int $options): bool
97
- {
98
- if ('r' !== $mode) {
99
- if ($options & \STREAM_REPORT_ERRORS) {
100
- trigger_error(sprintf('Invalid mode "%s": only "r" is supported.', $mode), \E_USER_WARNING);
101
- }
102
-
103
- return false;
104
- }
105
-
106
- $context = stream_context_get_options($this->context)['symfony'] ?? null;
107
- $this->client = $context['client'] ?? null;
108
- $this->response = $context['response'] ?? null;
109
- $this->context = null;
110
-
111
- if (null !== $this->client && null !== $this->response) {
112
- return true;
113
- }
114
-
115
- if ($options & \STREAM_REPORT_ERRORS) {
116
- trigger_error('Missing options "client" or "response" in "symfony" stream context.', \E_USER_WARNING);
117
- }
118
-
119
- return false;
120
- }
121
-
122
- public function stream_read(int $count)
123
- {
124
- if (\is_resource($this->content)) {
125
- // Empty the internal activity list
126
- foreach ($this->client->stream([$this->response], 0) as $chunk) {
127
- try {
128
- if (!$chunk->isTimeout() && $chunk->isFirst()) {
129
- $this->response->getStatusCode(); // ignore 3/4/5xx
130
- }
131
- } catch (ExceptionInterface $e) {
132
- trigger_error($e->getMessage(), \E_USER_WARNING);
133
-
134
- return false;
135
- }
136
- }
137
-
138
- if (0 !== fseek($this->content, $this->offset)) {
139
- return false;
140
- }
141
-
142
- if ('' !== $data = fread($this->content, $count)) {
143
- fseek($this->content, 0, \SEEK_END);
144
- $this->offset += \strlen($data);
145
-
146
- return $data;
147
- }
148
- }
149
-
150
- if (\is_string($this->content)) {
151
- if (\strlen($this->content) <= $count) {
152
- $data = $this->content;
153
- $this->content = null;
154
- } else {
155
- $data = substr($this->content, 0, $count);
156
- $this->content = substr($this->content, $count);
157
- }
158
- $this->offset += \strlen($data);
159
-
160
- return $data;
161
- }
162
-
163
- foreach ($this->client->stream([$this->response], $this->blocking ? $this->timeout : 0) as $chunk) {
164
- try {
165
- $this->eof = true;
166
- $this->eof = !$chunk->isTimeout();
167
- $this->eof = $chunk->isLast();
168
-
169
- if ($chunk->isFirst()) {
170
- $this->response->getStatusCode(); // ignore 3/4/5xx
171
- }
172
-
173
- if ('' !== $data = $chunk->getContent()) {
174
- if (\strlen($data) > $count) {
175
- if (null === $this->content) {
176
- $this->content = substr($data, $count);
177
- }
178
- $data = substr($data, 0, $count);
179
- }
180
- $this->offset += \strlen($data);
181
-
182
- return $data;
183
- }
184
- } catch (ExceptionInterface $e) {
185
- trigger_error($e->getMessage(), \E_USER_WARNING);
186
-
187
- return false;
188
- }
189
- }
190
-
191
- return '';
192
- }
193
-
194
- public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
195
- {
196
- if (\STREAM_OPTION_BLOCKING === $option) {
197
- $this->blocking = (bool) $arg1;
198
- } elseif (\STREAM_OPTION_READ_TIMEOUT === $option) {
199
- $this->timeout = $arg1 + $arg2 / 1e6;
200
- } else {
201
- return false;
202
- }
203
-
204
- return true;
205
- }
206
-
207
- public function stream_tell(): int
208
- {
209
- return $this->offset;
210
- }
211
-
212
- public function stream_eof(): bool
213
- {
214
- return $this->eof && !\is_string($this->content);
215
- }
216
-
217
- public function stream_seek(int $offset, int $whence = \SEEK_SET): bool
218
- {
219
- if (!\is_resource($this->content) || 0 !== fseek($this->content, 0, \SEEK_END)) {
220
- return false;
221
- }
222
-
223
- $size = ftell($this->content);
224
-
225
- if (\SEEK_CUR === $whence) {
226
- $offset += $this->offset;
227
- }
228
-
229
- if (\SEEK_END === $whence || $size < $offset) {
230
- foreach ($this->client->stream([$this->response]) as $chunk) {
231
- try {
232
- if ($chunk->isFirst()) {
233
- $this->response->getStatusCode(); // ignore 3/4/5xx
234
- }
235
-
236
- // Chunks are buffered in $this->content already
237
- $size += \strlen($chunk->getContent());
238
-
239
- if (\SEEK_END !== $whence && $offset <= $size) {
240
- break;
241
- }
242
- } catch (ExceptionInterface $e) {
243
- trigger_error($e->getMessage(), \E_USER_WARNING);
244
-
245
- return false;
246
- }
247
- }
248
-
249
- if (\SEEK_END === $whence) {
250
- $offset += $size;
251
- }
252
- }
253
-
254
- if (0 <= $offset && $offset <= $size) {
255
- $this->eof = false;
256
- $this->offset = $offset;
257
-
258
- return true;
259
- }
260
-
261
- return false;
262
- }
263
-
264
- public function stream_cast(int $castAs)
265
- {
266
- if (\STREAM_CAST_FOR_SELECT === $castAs) {
267
- $this->response->getHeaders(false);
268
-
269
- return (\is_callable($this->handle) ? ($this->handle)() : $this->handle) ?? false;
270
- }
271
-
272
- return false;
273
- }
274
-
275
- public function stream_stat(): array
276
- {
277
- try {
278
- $headers = $this->response->getHeaders(false);
279
- } catch (ExceptionInterface $e) {
280
- trigger_error($e->getMessage(), \E_USER_WARNING);
281
- $headers = [];
282
- }
283
-
284
- return [
285
- 'dev' => 0,
286
- 'ino' => 0,
287
- 'mode' => 33060,
288
- 'nlink' => 0,
289
- 'uid' => 0,
290
- 'gid' => 0,
291
- 'rdev' => 0,
292
- 'size' => (int) ($headers['content-length'][0] ?? -1),
293
- 'atime' => 0,
294
- 'mtime' => strtotime($headers['last-modified'][0] ?? '') ?: 0,
295
- 'ctime' => 0,
296
- 'blksize' => 0,
297
- 'blocks' => 0,
298
- ];
299
- }
300
-
301
- private function __construct()
302
- {
303
- }
304
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/StreamableInterface.php DELETED
@@ -1,35 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
15
- use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
16
- use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
17
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
18
-
19
- /**
20
- * @author Nicolas Grekas <p@tchwork.com>
21
- */
22
- interface StreamableInterface
23
- {
24
- /**
25
- * Casts the response to a PHP stream resource.
26
- *
27
- * @return resource
28
- *
29
- * @throws TransportExceptionInterface When a network error occurs
30
- * @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
31
- * @throws ClientExceptionInterface On a 4xx when $throw is true
32
- * @throws ServerExceptionInterface On a 5xx when $throw is true
33
- */
34
- public function toStream(bool $throw = true);
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/TraceableResponse.php DELETED
@@ -1,219 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Symfony\Component\HttpClient\Chunk\ErrorChunk;
15
- use Symfony\Component\HttpClient\Exception\ClientException;
16
- use Symfony\Component\HttpClient\Exception\RedirectionException;
17
- use Symfony\Component\HttpClient\Exception\ServerException;
18
- use Symfony\Component\HttpClient\TraceableHttpClient;
19
- use Symfony\Component\Stopwatch\StopwatchEvent;
20
- use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
21
- use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
22
- use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
23
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
24
- use Symfony\Contracts\HttpClient\HttpClientInterface;
25
- use Symfony\Contracts\HttpClient\ResponseInterface;
26
-
27
- /**
28
- * @author Nicolas Grekas <p@tchwork.com>
29
- *
30
- * @internal
31
- */
32
- class TraceableResponse implements ResponseInterface, StreamableInterface
33
- {
34
- private $client;
35
- private $response;
36
- private $content;
37
- private $event;
38
-
39
- public function __construct(HttpClientInterface $client, ResponseInterface $response, &$content, StopwatchEvent $event = null)
40
- {
41
- $this->client = $client;
42
- $this->response = $response;
43
- $this->content = &$content;
44
- $this->event = $event;
45
- }
46
-
47
- public function __sleep(): array
48
- {
49
- throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
50
- }
51
-
52
- public function __wakeup()
53
- {
54
- throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
55
- }
56
-
57
- public function __destruct()
58
- {
59
- try {
60
- $this->response->__destruct();
61
- } finally {
62
- if ($this->event && $this->event->isStarted()) {
63
- $this->event->stop();
64
- }
65
- }
66
- }
67
-
68
- public function getStatusCode(): int
69
- {
70
- try {
71
- return $this->response->getStatusCode();
72
- } finally {
73
- if ($this->event && $this->event->isStarted()) {
74
- $this->event->lap();
75
- }
76
- }
77
- }
78
-
79
- public function getHeaders(bool $throw = true): array
80
- {
81
- try {
82
- return $this->response->getHeaders($throw);
83
- } finally {
84
- if ($this->event && $this->event->isStarted()) {
85
- $this->event->lap();
86
- }
87
- }
88
- }
89
-
90
- public function getContent(bool $throw = true): string
91
- {
92
- try {
93
- if (false === $this->content) {
94
- return $this->response->getContent($throw);
95
- }
96
-
97
- return $this->content = $this->response->getContent(false);
98
- } finally {
99
- if ($this->event && $this->event->isStarted()) {
100
- $this->event->stop();
101
- }
102
- if ($throw) {
103
- $this->checkStatusCode($this->response->getStatusCode());
104
- }
105
- }
106
- }
107
-
108
- public function toArray(bool $throw = true): array
109
- {
110
- try {
111
- if (false === $this->content) {
112
- return $this->response->toArray($throw);
113
- }
114
-
115
- return $this->content = $this->response->toArray(false);
116
- } finally {
117
- if ($this->event && $this->event->isStarted()) {
118
- $this->event->stop();
119
- }
120
- if ($throw) {
121
- $this->checkStatusCode($this->response->getStatusCode());
122
- }
123
- }
124
- }
125
-
126
- public function cancel(): void
127
- {
128
- $this->response->cancel();
129
-
130
- if ($this->event && $this->event->isStarted()) {
131
- $this->event->stop();
132
- }
133
- }
134
-
135
- public function getInfo(string $type = null)
136
- {
137
- return $this->response->getInfo($type);
138
- }
139
-
140
- /**
141
- * Casts the response to a PHP stream resource.
142
- *
143
- * @return resource
144
- *
145
- * @throws TransportExceptionInterface When a network error occurs
146
- * @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
147
- * @throws ClientExceptionInterface On a 4xx when $throw is true
148
- * @throws ServerExceptionInterface On a 5xx when $throw is true
149
- */
150
- public function toStream(bool $throw = true)
151
- {
152
- if ($throw) {
153
- // Ensure headers arrived
154
- $this->response->getHeaders(true);
155
- }
156
-
157
- if ($this->response instanceof StreamableInterface) {
158
- return $this->response->toStream(false);
159
- }
160
-
161
- return StreamWrapper::createResource($this->response, $this->client);
162
- }
163
-
164
- /**
165
- * @internal
166
- */
167
- public static function stream(HttpClientInterface $client, iterable $responses, ?float $timeout): \Generator
168
- {
169
- $wrappedResponses = [];
170
- $traceableMap = new \SplObjectStorage();
171
-
172
- foreach ($responses as $r) {
173
- if (!$r instanceof self) {
174
- throw new \TypeError(sprintf('"%s::stream()" expects parameter 1 to be an iterable of TraceableResponse objects, "%s" given.', TraceableHttpClient::class, get_debug_type($r)));
175
- }
176
-
177
- $traceableMap[$r->response] = $r;
178
- $wrappedResponses[] = $r->response;
179
- if ($r->event && !$r->event->isStarted()) {
180
- $r->event->start();
181
- }
182
- }
183
-
184
- foreach ($client->stream($wrappedResponses, $timeout) as $r => $chunk) {
185
- if ($traceableMap[$r]->event && $traceableMap[$r]->event->isStarted()) {
186
- try {
187
- if ($chunk->isTimeout() || !$chunk->isLast()) {
188
- $traceableMap[$r]->event->lap();
189
- } else {
190
- $traceableMap[$r]->event->stop();
191
- }
192
- } catch (TransportExceptionInterface $e) {
193
- $traceableMap[$r]->event->stop();
194
- if ($chunk instanceof ErrorChunk) {
195
- $chunk->didThrow(false);
196
- } else {
197
- $chunk = new ErrorChunk($chunk->getOffset(), $e);
198
- }
199
- }
200
- }
201
- yield $traceableMap[$r] => $chunk;
202
- }
203
- }
204
-
205
- private function checkStatusCode(int $code)
206
- {
207
- if (500 <= $code) {
208
- throw new ServerException($this);
209
- }
210
-
211
- if (400 <= $code) {
212
- throw new ClientException($this);
213
- }
214
-
215
- if (300 <= $code) {
216
- throw new RedirectionException($this);
217
- }
218
- }
219
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Response/TransportResponseTrait.php DELETED
@@ -1,312 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Response;
13
-
14
- use Symfony\Component\HttpClient\Chunk\DataChunk;
15
- use Symfony\Component\HttpClient\Chunk\ErrorChunk;
16
- use Symfony\Component\HttpClient\Chunk\FirstChunk;
17
- use Symfony\Component\HttpClient\Chunk\LastChunk;
18
- use Symfony\Component\HttpClient\Exception\TransportException;
19
- use Symfony\Component\HttpClient\Internal\ClientState;
20
-
21
- /**
22
- * Implements common logic for transport-level response classes.
23
- *
24
- * @author Nicolas Grekas <p@tchwork.com>
25
- *
26
- * @internal
27
- */
28
- trait TransportResponseTrait
29
- {
30
- private $canary;
31
- private $headers = [];
32
- private $info = [
33
- 'response_headers' => [],
34
- 'http_code' => 0,
35
- 'error' => null,
36
- 'canceled' => false,
37
- ];
38
-
39
- /** @var object|resource */
40
- private $handle;
41
- private $id;
42
- private $timeout = 0;
43
- private $inflate;
44
- private $finalInfo;
45
- private $logger;
46
-
47
- /**
48
- * {@inheritdoc}
49
- */
50
- public function getStatusCode(): int
51
- {
52
- if ($this->initializer) {
53
- self::initialize($this);
54
- }
55
-
56
- return $this->info['http_code'];
57
- }
58
-
59
- /**
60
- * {@inheritdoc}
61
- */
62
- public function getHeaders(bool $throw = true): array
63
- {
64
- if ($this->initializer) {
65
- self::initialize($this);
66
- }
67
-
68
- if ($throw) {
69
- $this->checkStatusCode();
70
- }
71
-
72
- return $this->headers;
73
- }
74
-
75
- /**
76
- * {@inheritdoc}
77
- */
78
- public function cancel(): void
79
- {
80
- $this->info['canceled'] = true;
81
- $this->info['error'] = 'Response has been canceled.';
82
- $this->close();
83
- }
84
-
85
- /**
86
- * Closes the response and all its network handles.
87
- */
88
- protected function close(): void
89
- {
90
- $this->canary->cancel();
91
- $this->inflate = null;
92
- }
93
-
94
- /**
95
- * Adds pending responses to the activity list.
96
- */
97
- abstract protected static function schedule(self $response, array &$runningResponses): void;
98
-
99
- /**
100
- * Performs all pending non-blocking operations.
101
- */
102
- abstract protected static function perform(ClientState $multi, array &$responses): void;
103
-
104
- /**
105
- * Waits for network activity.
106
- */
107
- abstract protected static function select(ClientState $multi, float $timeout): int;
108
-
109
- private static function addResponseHeaders(array $responseHeaders, array &$info, array &$headers, string &$debug = ''): void
110
- {
111
- foreach ($responseHeaders as $h) {
112
- if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? (\d\d\d)(?: |$)#', $h, $m)) {
113
- if ($headers) {
114
- $debug .= "< \r\n";
115
- $headers = [];
116
- }
117
- $info['http_code'] = (int) $m[1];
118
- } elseif (2 === \count($m = explode(':', $h, 2))) {
119
- $headers[strtolower($m[0])][] = ltrim($m[1]);
120
- }
121
-
122
- $debug .= "< {$h}\r\n";
123
- $info['response_headers'][] = $h;
124
- }
125
-
126
- $debug .= "< \r\n";
127
- }
128
-
129
- /**
130
- * Ensures the request is always sent and that the response code was checked.
131
- */
132
- private function doDestruct()
133
- {
134
- $this->shouldBuffer = true;
135
-
136
- if ($this->initializer && null === $this->info['error']) {
137
- self::initialize($this);
138
- $this->checkStatusCode();
139
- }
140
- }
141
-
142
- /**
143
- * Implements an event loop based on a buffer activity queue.
144
- *
145
- * @param iterable<array-key, self> $responses
146
- *
147
- * @internal
148
- */
149
- public static function stream(iterable $responses, float $timeout = null): \Generator
150
- {
151
- $runningResponses = [];
152
-
153
- foreach ($responses as $response) {
154
- self::schedule($response, $runningResponses);
155
- }
156
-
157
- $lastActivity = microtime(true);
158
- $elapsedTimeout = 0;
159
-
160
- if ($fromLastTimeout = 0.0 === $timeout && '-0' === (string) $timeout) {
161
- $timeout = null;
162
- } elseif ($fromLastTimeout = 0 > $timeout) {
163
- $timeout = -$timeout;
164
- }
165
-
166
- while (true) {
167
- $hasActivity = false;
168
- $timeoutMax = 0;
169
- $timeoutMin = $timeout ?? \INF;
170
-
171
- /** @var ClientState $multi */
172
- foreach ($runningResponses as $i => [$multi]) {
173
- $responses = &$runningResponses[$i][1];
174
- self::perform($multi, $responses);
175
-
176
- foreach ($responses as $j => $response) {
177
- $timeoutMax = $timeout ?? max($timeoutMax, $response->timeout);
178
- $timeoutMin = min($timeoutMin, $response->timeout, 1);
179
- $chunk = false;
180
-
181
- if ($fromLastTimeout && null !== $multi->lastTimeout) {
182
- $elapsedTimeout = microtime(true) - $multi->lastTimeout;
183
- }
184
-
185
- if (isset($multi->handlesActivity[$j])) {
186
- $multi->lastTimeout = null;
187
- } elseif (!isset($multi->openHandles[$j])) {
188
- unset($responses[$j]);
189
- continue;
190
- } elseif ($elapsedTimeout >= $timeoutMax) {
191
- $multi->handlesActivity[$j] = [new ErrorChunk($response->offset, sprintf('Idle timeout reached for "%s".', $response->getInfo('url')))];
192
- $multi->lastTimeout ?? $multi->lastTimeout = $lastActivity;
193
- } else {
194
- continue;
195
- }
196
-
197
- while ($multi->handlesActivity[$j] ?? false) {
198
- $hasActivity = true;
199
- $elapsedTimeout = 0;
200
-
201
- if (\is_string($chunk = array_shift($multi->handlesActivity[$j]))) {
202
- if (null !== $response->inflate && false === $chunk = @inflate_add($response->inflate, $chunk)) {
203
- $multi->handlesActivity[$j] = [null, new TransportException(sprintf('Error while processing content unencoding for "%s".', $response->getInfo('url')))];
204
- continue;
205
- }
206
-
207
- if ('' !== $chunk && null !== $response->content && \strlen($chunk) !== fwrite($response->content, $chunk)) {
208
- $multi->handlesActivity[$j] = [null, new TransportException(sprintf('Failed writing %d bytes to the response buffer.', \strlen($chunk)))];
209
- continue;
210
- }
211
-
212
- $chunkLen = \strlen($chunk);
213
- $chunk = new DataChunk($response->offset, $chunk);
214
- $response->offset += $chunkLen;
215
- } elseif (null === $chunk) {
216
- $e = $multi->handlesActivity[$j][0];
217
- unset($responses[$j], $multi->handlesActivity[$j]);
218
- $response->close();
219
-
220
- if (null !== $e) {
221
- $response->info['error'] = $e->getMessage();
222
-
223
- if ($e instanceof \Error) {
224
- throw $e;
225
- }
226
-
227
- $chunk = new ErrorChunk($response->offset, $e);
228
- } else {
229
- if (0 === $response->offset && null === $response->content) {
230
- $response->content = fopen('php://memory', 'w+');
231
- }
232
-
233
- $chunk = new LastChunk($response->offset);
234
- }
235
- } elseif ($chunk instanceof ErrorChunk) {
236
- unset($responses[$j]);
237
- $elapsedTimeout = $timeoutMax;
238
- } elseif ($chunk instanceof FirstChunk) {
239
- if ($response->logger) {
240
- $info = $response->getInfo();
241
- $response->logger->info(sprintf('Response: "%s %s"', $info['http_code'], $info['url']));
242
- }
243
-
244
- $response->inflate = \extension_loaded('zlib') && $response->inflate && 'gzip' === ($response->headers['content-encoding'][0] ?? null) ? inflate_init(\ZLIB_ENCODING_GZIP) : null;
245
-
246
- if ($response->shouldBuffer instanceof \Closure) {
247
- try {
248
- $response->shouldBuffer = ($response->shouldBuffer)($response->headers);
249
-
250
- if (null !== $response->info['error']) {
251
- throw new TransportException($response->info['error']);
252
- }
253
- } catch (\Throwable $e) {
254
- $response->close();
255
- $multi->handlesActivity[$j] = [null, $e];
256
- }
257
- }
258
-
259
- if (true === $response->shouldBuffer) {
260
- $response->content = fopen('php://temp', 'w+');
261
- } elseif (\is_resource($response->shouldBuffer)) {
262
- $response->content = $response->shouldBuffer;
263
- }
264
- $response->shouldBuffer = null;
265
-
266
- yield $response => $chunk;
267
-
268
- if ($response->initializer && null === $response->info['error']) {
269
- // Ensure the HTTP status code is always checked
270
- $response->getHeaders(true);
271
- }
272
-
273
- continue;
274
- }
275
-
276
- yield $response => $chunk;
277
- }
278
-
279
- unset($multi->handlesActivity[$j]);
280
-
281
- if ($chunk instanceof ErrorChunk && !$chunk->didThrow()) {
282
- // Ensure transport exceptions are always thrown
283
- $chunk->getContent();
284
- }
285
- }
286
-
287
- if (!$responses) {
288
- unset($runningResponses[$i]);
289
- }
290
-
291
- // Prevent memory leaks
292
- $multi->handlesActivity = $multi->handlesActivity ?: [];
293
- $multi->openHandles = $multi->openHandles ?: [];
294
- }
295
-
296
- if (!$runningResponses) {
297
- break;
298
- }
299
-
300
- if ($hasActivity) {
301
- $lastActivity = microtime(true);
302
- continue;
303
- }
304
-
305
- if (-1 === self::select($multi, min($timeoutMin, $timeoutMax - $elapsedTimeout))) {
306
- usleep(min(500, 1E6 * $timeoutMin));
307
- }
308
-
309
- $elapsedTimeout = microtime(true) - $lastActivity;
310
- }
311
- }
312
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Retry/GenericRetryStrategy.php DELETED
@@ -1,115 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Retry;
13
-
14
- use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
15
- use Symfony\Component\HttpClient\Response\AsyncContext;
16
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
17
-
18
- /**
19
- * Decides to retry the request when HTTP status codes belong to the given list of codes.
20
- *
21
- * @author Jérémy Derussé <jeremy@derusse.com>
22
- */
23
- class GenericRetryStrategy implements RetryStrategyInterface
24
- {
25
- public const IDEMPOTENT_METHODS = ['GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'];
26
- public const DEFAULT_RETRY_STATUS_CODES = [
27
- 0 => self::IDEMPOTENT_METHODS, // for transport exceptions
28
- 423,
29
- 425,
30
- 429,
31
- 500 => self::IDEMPOTENT_METHODS,
32
- 502,
33
- 503,
34
- 504 => self::IDEMPOTENT_METHODS,
35
- 507 => self::IDEMPOTENT_METHODS,
36
- 510 => self::IDEMPOTENT_METHODS,
37
- ];
38
-
39
- private $statusCodes;
40
- private $delayMs;
41
- private $multiplier;
42
- private $maxDelayMs;
43
- private $jitter;
44
-
45
- /**
46
- * @param array $statusCodes List of HTTP status codes that trigger a retry
47
- * @param int $delayMs Amount of time to delay (or the initial value when multiplier is used)
48
- * @param float $multiplier Multiplier to apply to the delay each time a retry occurs
49
- * @param int $maxDelayMs Maximum delay to allow (0 means no maximum)
50
- * @param float $jitter Probability of randomness int delay (0 = none, 1 = 100% random)
51
- */
52
- public function __construct(array $statusCodes = self::DEFAULT_RETRY_STATUS_CODES, int $delayMs = 1000, float $multiplier = 2.0, int $maxDelayMs = 0, float $jitter = 0.1)
53
- {
54
- $this->statusCodes = $statusCodes;
55
-
56
- if ($delayMs < 0) {
57
- throw new InvalidArgumentException(sprintf('Delay must be greater than or equal to zero: "%s" given.', $delayMs));
58
- }
59
- $this->delayMs = $delayMs;
60
-
61
- if ($multiplier < 1) {
62
- throw new InvalidArgumentException(sprintf('Multiplier must be greater than or equal to one: "%s" given.', $multiplier));
63
- }
64
- $this->multiplier = $multiplier;
65
-
66
- if ($maxDelayMs < 0) {
67
- throw new InvalidArgumentException(sprintf('Max delay must be greater than or equal to zero: "%s" given.', $maxDelayMs));
68
- }
69
- $this->maxDelayMs = $maxDelayMs;
70
-
71
- if ($jitter < 0 || $jitter > 1) {
72
- throw new InvalidArgumentException(sprintf('Jitter must be between 0 and 1: "%s" given.', $jitter));
73
- }
74
- $this->jitter = $jitter;
75
- }
76
-
77
- public function shouldRetry(AsyncContext $context, ?string $responseContent, ?TransportExceptionInterface $exception): ?bool
78
- {
79
- $statusCode = $context->getStatusCode();
80
- if (\in_array($statusCode, $this->statusCodes, true)) {
81
- return true;
82
- }
83
- if (isset($this->statusCodes[$statusCode]) && \is_array($this->statusCodes[$statusCode])) {
84
- return \in_array($context->getInfo('http_method'), $this->statusCodes[$statusCode], true);
85
- }
86
- if (null === $exception) {
87
- return false;
88
- }
89
-
90
- if (\in_array(0, $this->statusCodes, true)) {
91
- return true;
92
- }
93
- if (isset($this->statusCodes[0]) && \is_array($this->statusCodes[0])) {
94
- return \in_array($context->getInfo('http_method'), $this->statusCodes[0], true);
95
- }
96
-
97
- return false;
98
- }
99
-
100
- public function getDelay(AsyncContext $context, ?string $responseContent, ?TransportExceptionInterface $exception): int
101
- {
102
- $delay = $this->delayMs * $this->multiplier ** $context->getInfo('retry_count');
103
-
104
- if ($this->jitter > 0) {
105
- $randomness = $delay * $this->jitter;
106
- $delay = $delay + random_int(-$randomness, +$randomness);
107
- }
108
-
109
- if ($delay > $this->maxDelayMs && 0 !== $this->maxDelayMs) {
110
- return $this->maxDelayMs;
111
- }
112
-
113
- return (int) $delay;
114
- }
115
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/Retry/RetryStrategyInterface.php DELETED
@@ -1,36 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient\Retry;
13
-
14
- use Symfony\Component\HttpClient\Response\AsyncContext;
15
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
16
-
17
- /**
18
- * @author Jérémy Derussé <jeremy@derusse.com>
19
- * @author Nicolas Grekas <p@tchwork.com>
20
- */
21
- interface RetryStrategyInterface
22
- {
23
- /**
24
- * Returns whether the request should be retried.
25
- *
26
- * @param ?string $responseContent Null is passed when the body did not arrive yet
27
- *
28
- * @return bool|null Returns null to signal that the body is required to take a decision
29
- */
30
- public function shouldRetry(AsyncContext $context, ?string $responseContent, ?TransportExceptionInterface $exception): ?bool;
31
-
32
- /**
33
- * Returns the time to wait in milliseconds.
34
- */
35
- public function getDelay(AsyncContext $context, ?string $responseContent, ?TransportExceptionInterface $exception): int;
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/RetryableHttpClient.php DELETED
@@ -1,169 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Psr\Log\LoggerInterface;
15
- use Psr\Log\NullLogger;
16
- use Symfony\Component\HttpClient\Response\AsyncContext;
17
- use Symfony\Component\HttpClient\Response\AsyncResponse;
18
- use Symfony\Component\HttpClient\Retry\GenericRetryStrategy;
19
- use Symfony\Component\HttpClient\Retry\RetryStrategyInterface;
20
- use Symfony\Contracts\HttpClient\ChunkInterface;
21
- use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
22
- use Symfony\Contracts\HttpClient\HttpClientInterface;
23
- use Symfony\Contracts\HttpClient\ResponseInterface;
24
- use Symfony\Contracts\Service\ResetInterface;
25
-
26
- /**
27
- * Automatically retries failing HTTP requests.
28
- *
29
- * @author Jérémy Derussé <jeremy@derusse.com>
30
- */
31
- class RetryableHttpClient implements HttpClientInterface, ResetInterface
32
- {
33
- use AsyncDecoratorTrait;
34
-
35
- private $strategy;
36
- private $maxRetries;
37
- private $logger;
38
-
39
- /**
40
- * @param int $maxRetries The maximum number of times to retry
41
- */
42
- public function __construct(HttpClientInterface $client, RetryStrategyInterface $strategy = null, int $maxRetries = 3, LoggerInterface $logger = null)
43
- {
44
- $this->client = $client;
45
- $this->strategy = $strategy ?? new GenericRetryStrategy();
46
- $this->maxRetries = $maxRetries;
47
- $this->logger = $logger ?? new NullLogger();
48
- }
49
-
50
- public function request(string $method, string $url, array $options = []): ResponseInterface
51
- {
52
- if ($this->maxRetries <= 0) {
53
- return new AsyncResponse($this->client, $method, $url, $options);
54
- }
55
-
56
- $retryCount = 0;
57
- $content = '';
58
- $firstChunk = null;
59
-
60
- return new AsyncResponse($this->client, $method, $url, $options, function (ChunkInterface $chunk, AsyncContext $context) use ($method, $url, $options, &$retryCount, &$content, &$firstChunk) {
61
- $exception = null;
62
- try {
63
- if ($chunk->isTimeout() || null !== $chunk->getInformationalStatus() || $context->getInfo('canceled')) {
64
- yield $chunk;
65
-
66
- return;
67
- }
68
- } catch (TransportExceptionInterface $exception) {
69
- // catch TransportExceptionInterface to send it to the strategy
70
- }
71
- if (null !== $exception) {
72
- // always retry request that fail to resolve DNS
73
- if ('' !== $context->getInfo('primary_ip')) {
74
- $shouldRetry = $this->strategy->shouldRetry($context, null, $exception);
75
- if (null === $shouldRetry) {
76
- throw new \LogicException(sprintf('The "%s::shouldRetry()" method must not return null when called with an exception.', \get_class($this->strategy)));
77
- }
78
-
79
- if (false === $shouldRetry) {
80
- yield from $this->passthru($context, $firstChunk, $content, $chunk);
81
-
82
- return;
83
- }
84
- }
85
- } elseif ($chunk->isFirst()) {
86
- if (false === $shouldRetry = $this->strategy->shouldRetry($context, null, null)) {
87
- yield from $this->passthru($context, $firstChunk, $content, $chunk);
88
-
89
- return;
90
- }
91
-
92
- // Body is needed to decide
93
- if (null === $shouldRetry) {
94
- $firstChunk = $chunk;
95
- $content = '';
96
-
97
- return;
98
- }
99
- } else {
100
- if (!$chunk->isLast()) {
101
- $content .= $chunk->getContent();
102
-
103
- return;
104
- }
105
-
106
- if (null === $shouldRetry = $this->strategy->shouldRetry($context, $content, null)) {
107
- throw new \LogicException(sprintf('The "%s::shouldRetry()" method must not return null when called with a body.', \get_class($this->strategy)));
108
- }
109
-
110
- if (false === $shouldRetry) {
111
- yield from $this->passthru($context, $firstChunk, $content, $chunk);
112
-
113
- return;
114
- }
115
- }
116
-
117
- $context->getResponse()->cancel();
118
-
119
- $delay = $this->getDelayFromHeader($context->getHeaders()) ?? $this->strategy->getDelay($context, !$exception && $chunk->isLast() ? $content : null, $exception);
120
- ++$retryCount;
121
-
122
- $this->logger->info('Try #{count} after {delay}ms'.($exception ? ': '.$exception->getMessage() : ', status code: '.$context->getStatusCode()), [
123
- 'count' => $retryCount,
124
- 'delay' => $delay,
125
- ]);
126
-
127
- $context->setInfo('retry_count', $retryCount);
128
- $context->replaceRequest($method, $url, $options);
129
- $context->pause($delay / 1000);
130
-
131
- if ($retryCount >= $this->maxRetries) {
132
- $context->passthru();
133
- }
134
- });
135
- }
136
-
137
- private function getDelayFromHeader(array $headers): ?int
138
- {
139
- if (null !== $after = $headers['retry-after'][0] ?? null) {
140
- if (is_numeric($after)) {
141
- return (int) $after * 1000;
142
- }
143
-
144
- if (false !== $time = strtotime($after)) {
145
- return max(0, $time - time()) * 1000;
146
- }
147
- }
148
-
149
- return null;
150
- }
151
-
152
- private function passthru(AsyncContext $context, ?ChunkInterface $firstChunk, string &$content, ChunkInterface $lastChunk): \Generator
153
- {
154
- $context->passthru();
155
-
156
- if (null !== $firstChunk) {
157
- yield $firstChunk;
158
- }
159
-
160
- if ('' !== $content) {
161
- $chunk = $context->createChunk($content);
162
- $content = '';
163
-
164
- yield $chunk;
165
- }
166
-
167
- yield $lastChunk;
168
- }
169
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/ScopingHttpClient.php DELETED
@@ -1,131 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Psr\Log\LoggerAwareInterface;
15
- use Psr\Log\LoggerInterface;
16
- use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
17
- use Symfony\Contracts\HttpClient\HttpClientInterface;
18
- use Symfony\Contracts\HttpClient\ResponseInterface;
19
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
20
- use Symfony\Contracts\Service\ResetInterface;
21
-
22
- /**
23
- * Auto-configure the default options based on the requested URL.
24
- *
25
- * @author Anthony Martin <anthony.martin@sensiolabs.com>
26
- */
27
- class ScopingHttpClient implements HttpClientInterface, ResetInterface, LoggerAwareInterface
28
- {
29
- use HttpClientTrait;
30
-
31
- private $client;
32
- private $defaultOptionsByRegexp;
33
- private $defaultRegexp;
34
-
35
- public function __construct(HttpClientInterface $client, array $defaultOptionsByRegexp, string $defaultRegexp = null)
36
- {
37
- $this->client = $client;
38
- $this->defaultOptionsByRegexp = $defaultOptionsByRegexp;
39
- $this->defaultRegexp = $defaultRegexp;
40
-
41
- if (null !== $defaultRegexp && !isset($defaultOptionsByRegexp[$defaultRegexp])) {
42
- throw new InvalidArgumentException(sprintf('No options are mapped to the provided "%s" default regexp.', $defaultRegexp));
43
- }
44
- }
45
-
46
- public static function forBaseUri(HttpClientInterface $client, string $baseUri, array $defaultOptions = [], string $regexp = null): self
47
- {
48
- if (null === $regexp) {
49
- $regexp = preg_quote(implode('', self::resolveUrl(self::parseUrl('.'), self::parseUrl($baseUri))));
50
- }
51
-
52
- $defaultOptions['base_uri'] = $baseUri;
53
-
54
- return new self($client, [$regexp => $defaultOptions], $regexp);
55
- }
56
-
57
- /**
58
- * {@inheritdoc}
59
- */
60
- public function request(string $method, string $url, array $options = []): ResponseInterface
61
- {
62
- $e = null;
63
- $url = self::parseUrl($url, $options['query'] ?? []);
64
-
65
- if (\is_string($options['base_uri'] ?? null)) {
66
- $options['base_uri'] = self::parseUrl($options['base_uri']);
67
- }
68
-
69
- try {
70
- $url = implode('', self::resolveUrl($url, $options['base_uri'] ?? null));
71
- } catch (InvalidArgumentException $e) {
72
- if (null === $this->defaultRegexp) {
73
- throw $e;
74
- }
75
-
76
- $defaultOptions = $this->defaultOptionsByRegexp[$this->defaultRegexp];
77
- $options = self::mergeDefaultOptions($options, $defaultOptions, true);
78
- if (\is_string($options['base_uri'] ?? null)) {
79
- $options['base_uri'] = self::parseUrl($options['base_uri']);
80
- }
81
- $url = implode('', self::resolveUrl($url, $options['base_uri'] ?? null, $defaultOptions['query'] ?? []));
82
- }
83
-
84
- foreach ($this->defaultOptionsByRegexp as $regexp => $defaultOptions) {
85
- if (preg_match("{{$regexp}}A", $url)) {
86
- if (null === $e || $regexp !== $this->defaultRegexp) {
87
- $options = self::mergeDefaultOptions($options, $defaultOptions, true);
88
- }
89
- break;
90
- }
91
- }
92
-
93
- return $this->client->request($method, $url, $options);
94
- }
95
-
96
- /**
97
- * {@inheritdoc}
98
- */
99
- public function stream($responses, float $timeout = null): ResponseStreamInterface
100
- {
101
- return $this->client->stream($responses, $timeout);
102
- }
103
-
104
- public function reset()
105
- {
106
- if ($this->client instanceof ResetInterface) {
107
- $this->client->reset();
108
- }
109
- }
110
-
111
- /**
112
- * {@inheritdoc}
113
- */
114
- public function setLogger(LoggerInterface $logger): void
115
- {
116
- if ($this->client instanceof LoggerAwareInterface) {
117
- $this->client->setLogger($logger);
118
- }
119
- }
120
-
121
- /**
122
- * {@inheritdoc}
123
- */
124
- public function withOptions(array $options): self
125
- {
126
- $clone = clone $this;
127
- $clone->client = $this->client->withOptions($options);
128
-
129
- return $clone;
130
- }
131
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/TraceableHttpClient.php DELETED
@@ -1,120 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Component\HttpClient;
13
-
14
- use Psr\Log\LoggerAwareInterface;
15
- use Psr\Log\LoggerInterface;
16
- use Symfony\Component\HttpClient\Response\ResponseStream;
17
- use Symfony\Component\HttpClient\Response\TraceableResponse;
18
- use Symfony\Component\Stopwatch\Stopwatch;
19
- use Symfony\Contracts\HttpClient\HttpClientInterface;
20
- use Symfony\Contracts\HttpClient\ResponseInterface;
21
- use Symfony\Contracts\HttpClient\ResponseStreamInterface;
22
- use Symfony\Contracts\Service\ResetInterface;
23
-
24
- /**
25
- * @author Jérémy Romey <jeremy@free-agent.fr>
26
- */
27
- final class TraceableHttpClient implements HttpClientInterface, ResetInterface, LoggerAwareInterface
28
- {
29
- private $client;
30
- private $stopwatch;
31
- private $tracedRequests;
32
-
33
- public function __construct(HttpClientInterface $client, Stopwatch $stopwatch = null)
34
- {
35
- $this->client = $client;
36
- $this->stopwatch = $stopwatch;
37
- $this->tracedRequests = new \ArrayObject();
38
- }
39
-
40
- /**
41
- * {@inheritdoc}
42
- */
43
- public function request(string $method, string $url, array $options = []): ResponseInterface
44
- {
45
- $content = null;
46
- $traceInfo = [];
47
- $this->tracedRequests[] = [
48
- 'method' => $method,
49
- 'url' => $url,
50
- 'options' => $options,
51
- 'info' => &$traceInfo,
52
- 'content' => &$content,
53
- ];
54
- $onProgress = $options['on_progress'] ?? null;
55
-
56
- if (false === ($options['extra']['trace_content'] ?? true)) {
57
- unset($content);
58
- $content = false;
59
- }
60
-
61
- $options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use (&$traceInfo, $onProgress) {
62
- $traceInfo = $info;
63
-
64
- if (null !== $onProgress) {
65
- $onProgress($dlNow, $dlSize, $info);
66
- }
67
- };
68
-
69
- return new TraceableResponse($this->client, $this->client->request($method, $url, $options), $content, null === $this->stopwatch ? null : $this->stopwatch->start("$method $url", 'http_client'));
70
- }
71
-
72
- /**
73
- * {@inheritdoc}
74
- */
75
- public function stream($responses, float $timeout = null): ResponseStreamInterface
76
- {
77
- if ($responses instanceof TraceableResponse) {
78
- $responses = [$responses];
79
- } elseif (!is_iterable($responses)) {
80
- throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of TraceableResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
81
- }
82
-
83
- return new ResponseStream(TraceableResponse::stream($this->client, $responses, $timeout));
84
- }
85
-
86
- public function getTracedRequests(): array
87
- {
88
- return $this->tracedRequests->getArrayCopy();
89
- }
90
-
91
- public function reset()
92
- {
93
- if ($this->client instanceof ResetInterface) {
94
- $this->client->reset();
95
- }
96
-
97
- $this->tracedRequests->exchangeArray([]);
98
- }
99
-
100
- /**
101
- * {@inheritdoc}
102
- */
103
- public function setLogger(LoggerInterface $logger): void
104
- {
105
- if ($this->client instanceof LoggerAwareInterface) {
106
- $this->client->setLogger($logger);
107
- }
108
- }
109
-
110
- /**
111
- * {@inheritdoc}
112
- */
113
- public function withOptions(array $options): self
114
- {
115
- $clone = clone $this;
116
- $clone->client = $this->client->withOptions($options);
117
-
118
- return $clone;
119
- }
120
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/http-client/composer.json DELETED
@@ -1,53 +0,0 @@
1
- {
2
- "name": "symfony/http-client",
3
- "type": "library",
4
- "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
5
- "homepage": "https://symfony.com",
6
- "license": "MIT",
7
- "authors": [
8
- {
9
- "name": "Nicolas Grekas",
10
- "email": "p@tchwork.com"
11
- },
12
- {
13
- "name": "Symfony Community",
14
- "homepage": "https://symfony.com/contributors"
15
- }
16
- ],
17
- "provide": {
18
- "php-http/async-client-implementation": "*",
19
- "php-http/client-implementation": "*",
20
- "psr/http-client-implementation": "1.0",
21
- "symfony/http-client-implementation": "2.4"
22
- },
23
- "require": {
24
- "php": ">=7.2.5",
25
- "psr/log": "^1|^2|^3",
26
- "symfony/deprecation-contracts": "^2.1|^3",
27
- "symfony/http-client-contracts": "^2.4",
28
- "symfony/polyfill-php73": "^1.11",
29
- "symfony/polyfill-php80": "^1.16",
30
- "symfony/service-contracts": "^1.0|^2|^3"
31
- },
32
- "require-dev": {
33
- "amphp/amp": "^2.5",
34
- "amphp/http-client": "^4.2.1",
35
- "amphp/http-tunnel": "^1.0",
36
- "amphp/socket": "^1.1",
37
- "guzzlehttp/promises": "^1.4",
38
- "nyholm/psr7": "^1.0",
39
- "php-http/httplug": "^1.0|^2.0",
40
- "psr/http-client": "^1.0",
41
- "symfony/dependency-injection": "^4.4|^5.0|^6.0",
42
- "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0",
43
- "symfony/process": "^4.4|^5.0|^6.0",
44
- "symfony/stopwatch": "^4.4|^5.0|^6.0"
45
- },
46
- "autoload": {
47
- "psr-4": { "Symfony\\Component\\HttpClient\\": "" },
48
- "exclude-from-classmap": [
49
- "/Tests/"
50
- ]
51
- },
52
- "minimum-stability": "dev"
53
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/.gitignore DELETED
@@ -1,3 +0,0 @@
1
- vendor/
2
- composer.lock
3
- phpunit.xml
 
 
 
src/vendor/symfony/service-contracts/Attribute/Required.php DELETED
@@ -1,25 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\Service\Attribute;
13
-
14
- /**
15
- * A required dependency.
16
- *
17
- * This attribute indicates that a property holds a required dependency. The annotated property or method should be
18
- * considered during the instantiation process of the containing class.
19
- *
20
- * @author Alexander M. Turek <me@derrabus.de>
21
- */
22
- #[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY)]
23
- final class Required
24
- {
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/Attribute/SubscribedService.php DELETED
@@ -1,33 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\Service\Attribute;
13
-
14
- use Symfony\Contracts\Service\ServiceSubscriberTrait;
15
-
16
- /**
17
- * Use with {@see ServiceSubscriberTrait} to mark a method's return type
18
- * as a subscribed service.
19
- *
20
- * @author Kevin Bond <kevinbond@gmail.com>
21
- */
22
- #[\Attribute(\Attribute::TARGET_METHOD)]
23
- final class SubscribedService
24
- {
25
- /**
26
- * @param string|null $key The key to use for the service
27
- * If null, use "ClassName::methodName"
28
- */
29
- public function __construct(
30
- public ?string $key = null
31
- ) {
32
- }
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/CHANGELOG.md DELETED
@@ -1,5 +0,0 @@
1
- CHANGELOG
2
- =========
3
-
4
- The changelog is maintained for all Symfony contracts at the following URL:
5
- https://github.com/symfony/contracts/blob/main/CHANGELOG.md
 
 
 
 
 
src/vendor/symfony/service-contracts/README.md DELETED
@@ -1,9 +0,0 @@
1
- Symfony Service Contracts
2
- =========================
3
-
4
- A set of abstractions extracted out of the Symfony components.
5
-
6
- Can be used to build on semantics that the Symfony components proved useful - and
7
- that already have battle tested implementations.
8
-
9
- See https://github.com/symfony/contracts/blob/main/README.md for more information.
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/ResetInterface.php DELETED
@@ -1,30 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\Service;
13
-
14
- /**
15
- * Provides a way to reset an object to its initial state.
16
- *
17
- * When calling the "reset()" method on an object, it should be put back to its
18
- * initial state. This usually means clearing any internal buffers and forwarding
19
- * the call to internal dependencies. All properties of the object should be put
20
- * back to the same state it had when it was first ready to use.
21
- *
22
- * This method could be called, for example, to recycle objects that are used as
23
- * services, so that they can be used to handle several requests in the same
24
- * process loop (note that we advise making your services stateless instead of
25
- * implementing this interface when possible.)
26
- */
27
- interface ResetInterface
28
- {
29
- public function reset();
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/ServiceLocatorTrait.php DELETED
@@ -1,128 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\Service;
13
-
14
- use Psr\Container\ContainerExceptionInterface;
15
- use Psr\Container\NotFoundExceptionInterface;
16
-
17
- // Help opcache.preload discover always-needed symbols
18
- class_exists(ContainerExceptionInterface::class);
19
- class_exists(NotFoundExceptionInterface::class);
20
-
21
- /**
22
- * A trait to help implement ServiceProviderInterface.
23
- *
24
- * @author Robin Chalas <robin.chalas@gmail.com>
25
- * @author Nicolas Grekas <p@tchwork.com>
26
- */
27
- trait ServiceLocatorTrait
28
- {
29
- private $factories;
30
- private $loading = [];
31
- private $providedTypes;
32
-
33
- /**
34
- * @param callable[] $factories
35
- */
36
- public function __construct(array $factories)
37
- {
38
- $this->factories = $factories;
39
- }
40
-
41
- /**
42
- * {@inheritdoc}
43
- *
44
- * @return bool
45
- */
46
- public function has(string $id)
47
- {
48
- return isset($this->factories[$id]);
49
- }
50
-
51
- /**
52
- * {@inheritdoc}
53
- *
54
- * @return mixed
55
- */
56
- public function get(string $id)
57
- {
58
- if (!isset($this->factories[$id])) {
59
- throw $this->createNotFoundException($id);
60
- }
61
-
62
- if (isset($this->loading[$id])) {
63
- $ids = array_values($this->loading);
64
- $ids = \array_slice($this->loading, array_search($id, $ids));
65
- $ids[] = $id;
66
-
67
- throw $this->createCircularReferenceException($id, $ids);
68
- }
69
-
70
- $this->loading[$id] = $id;
71
- try {
72
- return $this->factories[$id]($this);
73
- } finally {
74
- unset($this->loading[$id]);
75
- }
76
- }
77
-
78
- /**
79
- * {@inheritdoc}
80
- */
81
- public function getProvidedServices(): array
82
- {
83
- if (null === $this->providedTypes) {
84
- $this->providedTypes = [];
85
-
86
- foreach ($this->factories as $name => $factory) {
87
- if (!\is_callable($factory)) {
88
- $this->providedTypes[$name] = '?';
89
- } else {
90
- $type = (new \ReflectionFunction($factory))->getReturnType();
91
-
92
- $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').($type instanceof \ReflectionNamedType ? $type->getName() : $type) : '?';
93
- }
94
- }
95
- }
96
-
97
- return $this->providedTypes;
98
- }
99
-
100
- private function createNotFoundException(string $id): NotFoundExceptionInterface
101
- {
102
- if (!$alternatives = array_keys($this->factories)) {
103
- $message = 'is empty...';
104
- } else {
105
- $last = array_pop($alternatives);
106
- if ($alternatives) {
107
- $message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last);
108
- } else {
109
- $message = sprintf('only knows about the "%s" service.', $last);
110
- }
111
- }
112
-
113
- if ($this->loading) {
114
- $message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message);
115
- } else {
116
- $message = sprintf('Service "%s" not found: the current service locator %s', $id, $message);
117
- }
118
-
119
- return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface {
120
- };
121
- }
122
-
123
- private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface
124
- {
125
- return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface {
126
- };
127
- }
128
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/ServiceProviderInterface.php DELETED
@@ -1,36 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\Service;
13
-
14
- use Psr\Container\ContainerInterface;
15
-
16
- /**
17
- * A ServiceProviderInterface exposes the identifiers and the types of services provided by a container.
18
- *
19
- * @author Nicolas Grekas <p@tchwork.com>
20
- * @author Mateusz Sip <mateusz.sip@gmail.com>
21
- */
22
- interface ServiceProviderInterface extends ContainerInterface
23
- {
24
- /**
25
- * Returns an associative array of service types keyed by the identifiers provided by the current container.
26
- *
27
- * Examples:
28
- *
29
- * * ['logger' => 'Psr\Log\LoggerInterface'] means the object provides a service named "logger" that implements Psr\Log\LoggerInterface
30
- * * ['foo' => '?'] means the container provides service name "foo" of unspecified type
31
- * * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null
32
- *
33
- * @return string[] The provided service types, keyed by service names
34
- */
35
- public function getProvidedServices(): array;
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/ServiceSubscriberInterface.php DELETED
@@ -1,53 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\Service;
13
-
14
- /**
15
- * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method.
16
- *
17
- * The getSubscribedServices method returns an array of service types required by such instances,
18
- * optionally keyed by the service names used internally. Service types that start with an interrogation
19
- * mark "?" are optional, while the other ones are mandatory service dependencies.
20
- *
21
- * The injected service locators SHOULD NOT allow access to any other services not specified by the method.
22
- *
23
- * It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally.
24
- * This interface does not dictate any injection method for these service locators, although constructor
25
- * injection is recommended.
26
- *
27
- * @author Nicolas Grekas <p@tchwork.com>
28
- */
29
- interface ServiceSubscriberInterface
30
- {
31
- /**
32
- * Returns an array of service types required by such instances, optionally keyed by the service names used internally.
33
- *
34
- * For mandatory dependencies:
35
- *
36
- * * ['logger' => 'Psr\Log\LoggerInterface'] means the objects use the "logger" name
37
- * internally to fetch a service which must implement Psr\Log\LoggerInterface.
38
- * * ['loggers' => 'Psr\Log\LoggerInterface[]'] means the objects use the "loggers" name
39
- * internally to fetch an iterable of Psr\Log\LoggerInterface instances.
40
- * * ['Psr\Log\LoggerInterface'] is a shortcut for
41
- * * ['Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface']
42
- *
43
- * otherwise:
44
- *
45
- * * ['logger' => '?Psr\Log\LoggerInterface'] denotes an optional dependency
46
- * * ['loggers' => '?Psr\Log\LoggerInterface[]'] denotes an optional iterable dependency
47
- * * ['?Psr\Log\LoggerInterface'] is a shortcut for
48
- * * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface']
49
- *
50
- * @return string[] The required service types, optionally keyed by service names
51
- */
52
- public static function getSubscribedServices();
53
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/ServiceSubscriberTrait.php DELETED
@@ -1,115 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\Service;
13
-
14
- use Psr\Container\ContainerInterface;
15
- use Symfony\Contracts\Service\Attribute\SubscribedService;
16
-
17
- /**
18
- * Implementation of ServiceSubscriberInterface that determines subscribed services from
19
- * method return types. Service ids are available as "ClassName::methodName".
20
- *
21
- * @author Kevin Bond <kevinbond@gmail.com>
22
- */
23
- trait ServiceSubscriberTrait
24
- {
25
- /** @var ContainerInterface */
26
- protected $container;
27
-
28
- /**
29
- * {@inheritdoc}
30
- */
31
- public static function getSubscribedServices(): array
32
- {
33
- static $services;
34
-
35
- if (null !== $services) {
36
- return $services;
37
- }
38
-
39
- $services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : [];
40
- $attributeOptIn = false;
41
-
42
- if (\PHP_VERSION_ID >= 80000) {
43
- foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
44
- if (self::class !== $method->getDeclaringClass()->name) {
45
- continue;
46
- }
47
-
48
- if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) {
49
- continue;
50
- }
51
-
52
- if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
53
- throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name));
54
- }
55
-
56
- if (!$returnType = $method->getReturnType()) {
57
- throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class));
58
- }
59
-
60
- $serviceId = $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType;
61
-
62
- if ($returnType->allowsNull()) {
63
- $serviceId = '?'.$serviceId;
64
- }
65
-
66
- $services[$attribute->newInstance()->key ?? self::class.'::'.$method->name] = $serviceId;
67
- $attributeOptIn = true;
68
- }
69
- }
70
-
71
- if (!$attributeOptIn) {
72
- foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
73
- if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
74
- continue;
75
- }
76
-
77
- if (self::class !== $method->getDeclaringClass()->name) {
78
- continue;
79
- }
80
-
81
- if (!($returnType = $method->getReturnType()) instanceof \ReflectionNamedType) {
82
- continue;
83
- }
84
-
85
- if ($returnType->isBuiltin()) {
86
- continue;
87
- }
88
-
89
- if (\PHP_VERSION_ID >= 80000) {
90
- trigger_deprecation('symfony/service-contracts', '2.5', 'Using "%s" in "%s" without using the "%s" attribute on any method is deprecated.', ServiceSubscriberTrait::class, self::class, SubscribedService::class);
91
- }
92
-
93
- $services[self::class.'::'.$method->name] = '?'.($returnType instanceof \ReflectionNamedType ? $returnType->getName() : $returnType);
94
- }
95
- }
96
-
97
- return $services;
98
- }
99
-
100
- /**
101
- * @required
102
- *
103
- * @return ContainerInterface|null
104
- */
105
- public function setContainer(ContainerInterface $container)
106
- {
107
- $this->container = $container;
108
-
109
- if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) {
110
- return parent::setContainer($container);
111
- }
112
-
113
- return null;
114
- }
115
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php DELETED
@@ -1,95 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * This file is part of the Symfony package.
5
- *
6
- * (c) Fabien Potencier <fabien@symfony.com>
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
- namespace Symfony\Contracts\Service\Test;
13
-
14
- use PHPUnit\Framework\TestCase;
15
- use Psr\Container\ContainerInterface;
16
- use Symfony\Contracts\Service\ServiceLocatorTrait;
17
-
18
- abstract class ServiceLocatorTest extends TestCase
19
- {
20
- /**
21
- * @return ContainerInterface
22
- */
23
- protected function getServiceLocator(array $factories)
24
- {
25
- return new class($factories) implements ContainerInterface {
26
- use ServiceLocatorTrait;
27
- };
28
- }
29
-
30
- public function testHas()
31
- {
32
- $locator = $this->getServiceLocator([
33
- 'foo' => function () { return 'bar'; },
34
- 'bar' => function () { return 'baz'; },
35
- function () { return 'dummy'; },
36
- ]);
37
-
38
- $this->assertTrue($locator->has('foo'));
39
- $this->assertTrue($locator->has('bar'));
40
- $this->assertFalse($locator->has('dummy'));
41
- }
42
-
43
- public function testGet()
44
- {
45
- $locator = $this->getServiceLocator([
46
- 'foo' => function () { return 'bar'; },
47
- 'bar' => function () { return 'baz'; },
48
- ]);
49
-
50
- $this->assertSame('bar', $locator->get('foo'));
51
- $this->assertSame('baz', $locator->get('bar'));
52
- }
53
-
54
- public function testGetDoesNotMemoize()
55
- {
56
- $i = 0;
57
- $locator = $this->getServiceLocator([
58
- 'foo' => function () use (&$i) {
59
- ++$i;
60
-
61
- return 'bar';
62
- },
63
- ]);
64
-
65
- $this->assertSame('bar', $locator->get('foo'));
66
- $this->assertSame('bar', $locator->get('foo'));
67
- $this->assertSame(2, $i);
68
- }
69
-
70
- public function testThrowsOnUndefinedInternalService()
71
- {
72
- if (!$this->getExpectedException()) {
73
- $this->expectException(\Psr\Container\NotFoundExceptionInterface::class);
74
- $this->expectExceptionMessage('The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service.');
75
- }
76
- $locator = $this->getServiceLocator([
77
- 'foo' => function () use (&$locator) { return $locator->get('bar'); },
78
- ]);
79
-
80
- $locator->get('foo');
81
- }
82
-
83
- public function testThrowsOnCircularReference()
84
- {
85
- $this->expectException(\Psr\Container\ContainerExceptionInterface::class);
86
- $this->expectExceptionMessage('Circular reference detected for service "bar", path: "bar -> baz -> bar".');
87
- $locator = $this->getServiceLocator([
88
- 'foo' => function () use (&$locator) { return $locator->get('bar'); },
89
- 'bar' => function () use (&$locator) { return $locator->get('baz'); },
90
- 'baz' => function () use (&$locator) { return $locator->get('bar'); },
91
- ]);
92
-
93
- $locator->get('foo');
94
- }
95
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/vendor/symfony/service-contracts/composer.json DELETED
@@ -1,42 +0,0 @@
1
- {
2
- "name": "symfony/service-contracts",
3
- "type": "library",
4
- "description": "Generic abstractions related to writing services",
5
- "keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
6
- "homepage": "https://symfony.com",
7
- "license": "MIT",
8
- "authors": [
9
- {
10
- "name": "Nicolas Grekas",
11
- "email": "p@tchwork.com"
12
- },
13
- {
14
- "name": "Symfony Community",
15
- "homepage": "https://symfony.com/contributors"
16
- }
17
- ],
18
- "require": {
19
- "php": ">=7.2.5",
20
- "psr/container": "^1.1",
21
- "symfony/deprecation-contracts": "^2.1|^3"
22
- },
23
- "conflict": {
24
- "ext-psr": "<1.1|>=2"
25
- },
26
- "suggest": {
27
- "symfony/service-implementation": ""
28
- },
29
- "autoload": {
30
- "psr-4": { "Symfony\\Contracts\\Service\\": "" }
31
- },
32
- "minimum-stability": "dev",
33
- "extra": {
34
- "branch-alias": {
35
- "dev-main": "2.5-dev"
36
- },
37
- "thanks": {
38
- "name": "symfony/contracts",
39
- "url": "https://github.com/symfony/contracts"
40
- }
41
- }
42
- }