RequestOptions.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <?php
  2. namespace Stripe\Util;
  3. /**
  4. * @phpstan-type RequestOptionsArray array{api_key?: string, idempotency_key?: string, stripe_account?: string, stripe_version?: string, api_base?: string }
  5. * @psalm-type RequestOptionsArray = array{api_key?: string, idempotency_key?: string, stripe_account?: string, stripe_version?: string, api_base?: string }
  6. */
  7. class RequestOptions
  8. {
  9. /**
  10. * @var array<string> a list of headers that should be persisted across requests
  11. */
  12. public static $HEADERS_TO_PERSIST = [
  13. 'Stripe-Account',
  14. 'Stripe-Version',
  15. ];
  16. /** @var array<string, string> */
  17. public $headers;
  18. /** @var null|string */
  19. public $apiKey;
  20. /** @var null|string */
  21. public $apiBase;
  22. /**
  23. * @param null|string $key
  24. * @param array<string, string> $headers
  25. * @param null|string $base
  26. */
  27. public function __construct($key = null, $headers = [], $base = null)
  28. {
  29. $this->apiKey = $key;
  30. $this->headers = $headers;
  31. $this->apiBase = $base;
  32. }
  33. /**
  34. * @return array<string, string>
  35. */
  36. public function __debugInfo()
  37. {
  38. return [
  39. 'apiKey' => $this->redactedApiKey(),
  40. 'headers' => $this->headers,
  41. 'apiBase' => $this->apiBase,
  42. ];
  43. }
  44. /**
  45. * Unpacks an options array and merges it into the existing RequestOptions
  46. * object.
  47. *
  48. * @param null|array|RequestOptions|string $options a key => value array
  49. * @param bool $strict when true, forbid string form and arbitrary keys in array form
  50. *
  51. * @return RequestOptions
  52. */
  53. public function merge($options, $strict = false)
  54. {
  55. $other_options = self::parse($options, $strict);
  56. if (null === $other_options->apiKey) {
  57. $other_options->apiKey = $this->apiKey;
  58. }
  59. if (null === $other_options->apiBase) {
  60. $other_options->apiBase = $this->apiBase;
  61. }
  62. $other_options->headers = \array_merge($this->headers, $other_options->headers);
  63. return $other_options;
  64. }
  65. /**
  66. * Discards all headers that we don't want to persist across requests.
  67. */
  68. public function discardNonPersistentHeaders()
  69. {
  70. foreach ($this->headers as $k => $v) {
  71. if (!\in_array($k, self::$HEADERS_TO_PERSIST, true)) {
  72. unset($this->headers[$k]);
  73. }
  74. }
  75. }
  76. /**
  77. * Unpacks an options array into an RequestOptions object.
  78. *
  79. * @param null|array|RequestOptions|string $options a key => value array
  80. * @param bool $strict when true, forbid string form and arbitrary keys in array form
  81. *
  82. * @throws \Stripe\Exception\InvalidArgumentException
  83. *
  84. * @return RequestOptions
  85. */
  86. public static function parse($options, $strict = false)
  87. {
  88. if ($options instanceof self) {
  89. return clone $options;
  90. }
  91. if (null === $options) {
  92. return new RequestOptions(null, [], null);
  93. }
  94. if (\is_string($options)) {
  95. if ($strict) {
  96. $message = 'Do not pass a string for request options. If you want to set the '
  97. . 'API key, pass an array like ["api_key" => <apiKey>] instead.';
  98. throw new \Stripe\Exception\InvalidArgumentException($message);
  99. }
  100. return new RequestOptions($options, [], null);
  101. }
  102. if (\is_array($options)) {
  103. $headers = [];
  104. $key = null;
  105. $base = null;
  106. if (\array_key_exists('api_key', $options)) {
  107. $key = $options['api_key'];
  108. unset($options['api_key']);
  109. }
  110. if (\array_key_exists('idempotency_key', $options)) {
  111. $headers['Idempotency-Key'] = $options['idempotency_key'];
  112. unset($options['idempotency_key']);
  113. }
  114. if (\array_key_exists('stripe_account', $options)) {
  115. $headers['Stripe-Account'] = $options['stripe_account'];
  116. unset($options['stripe_account']);
  117. }
  118. if (\array_key_exists('stripe_version', $options)) {
  119. $headers['Stripe-Version'] = $options['stripe_version'];
  120. unset($options['stripe_version']);
  121. }
  122. if (\array_key_exists('api_base', $options)) {
  123. $base = $options['api_base'];
  124. unset($options['api_base']);
  125. }
  126. if ($strict && !empty($options)) {
  127. $message = 'Got unexpected keys in options array: ' . \implode(', ', \array_keys($options));
  128. throw new \Stripe\Exception\InvalidArgumentException($message);
  129. }
  130. return new RequestOptions($key, $headers, $base);
  131. }
  132. $message = 'The second argument to Stripe API method calls is an '
  133. . 'optional per-request apiKey, which must be a string, or '
  134. . 'per-request options, which must be an array. (HINT: you can set '
  135. . 'a global apiKey by "Stripe::setApiKey(<apiKey>)")';
  136. throw new \Stripe\Exception\InvalidArgumentException($message);
  137. }
  138. /** @return string */
  139. private function redactedApiKey()
  140. {
  141. if (null === $this->apiKey) {
  142. return '';
  143. }
  144. $pieces = \explode('_', $this->apiKey, 3);
  145. $last = \array_pop($pieces);
  146. $redactedLast = \strlen($last) > 4
  147. ? (\str_repeat('*', \strlen($last) - 4) . \substr($last, -4))
  148. : $last;
  149. $pieces[] = $redactedLast;
  150. return \implode('_', $pieces);
  151. }
  152. }