diff options
Diffstat (limited to 'vendor/stripe/stripe-php/lib/Util')
-rw-r--r-- | vendor/stripe/stripe-php/lib/Util/CaseInsensitiveArray.php | 72 | ||||
-rw-r--r-- | vendor/stripe/stripe-php/lib/Util/DefaultLogger.php | 29 | ||||
-rw-r--r-- | vendor/stripe/stripe-php/lib/Util/LoggerInterface.php | 34 | ||||
-rw-r--r-- | vendor/stripe/stripe-php/lib/Util/ObjectTypes.php | 103 | ||||
-rw-r--r-- | vendor/stripe/stripe-php/lib/Util/RandomGenerator.php | 36 | ||||
-rw-r--r-- | vendor/stripe/stripe-php/lib/Util/RequestOptions.php | 168 | ||||
-rw-r--r-- | vendor/stripe/stripe-php/lib/Util/Set.php | 44 | ||||
-rw-r--r-- | vendor/stripe/stripe-php/lib/Util/Util.php | 265 |
8 files changed, 751 insertions, 0 deletions
diff --git a/vendor/stripe/stripe-php/lib/Util/CaseInsensitiveArray.php b/vendor/stripe/stripe-php/lib/Util/CaseInsensitiveArray.php new file mode 100644 index 0000000..670ab0b --- /dev/null +++ b/vendor/stripe/stripe-php/lib/Util/CaseInsensitiveArray.php @@ -0,0 +1,72 @@ +<?php + +namespace Stripe\Util; + +/** + * CaseInsensitiveArray is an array-like class that ignores case for keys. + * + * It is used to store HTTP headers. Per RFC 2616, section 4.2: + * Each header field consists of a name followed by a colon (":") and the field value. Field names + * are case-insensitive. + * + * In the context of stripe-php, this is useful because the API will return headers with different + * case depending on whether HTTP/2 is used or not (with HTTP/2, headers are always in lowercase). + */ +class CaseInsensitiveArray implements \ArrayAccess, \Countable, \IteratorAggregate +{ + private $container = []; + + public function __construct($initial_array = []) + { + $this->container = \array_change_key_case($initial_array, \CASE_LOWER); + } + + public function count() + { + return \count($this->container); + } + + public function getIterator() + { + return new \ArrayIterator($this->container); + } + + public function offsetSet($offset, $value) + { + $offset = static::maybeLowercase($offset); + if (null === $offset) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + public function offsetExists($offset) + { + $offset = static::maybeLowercase($offset); + + return isset($this->container[$offset]); + } + + public function offsetUnset($offset) + { + $offset = static::maybeLowercase($offset); + unset($this->container[$offset]); + } + + public function offsetGet($offset) + { + $offset = static::maybeLowercase($offset); + + return isset($this->container[$offset]) ? $this->container[$offset] : null; + } + + private static function maybeLowercase($v) + { + if (\is_string($v)) { + return \strtolower($v); + } + + return $v; + } +} diff --git a/vendor/stripe/stripe-php/lib/Util/DefaultLogger.php b/vendor/stripe/stripe-php/lib/Util/DefaultLogger.php new file mode 100644 index 0000000..016cbe8 --- /dev/null +++ b/vendor/stripe/stripe-php/lib/Util/DefaultLogger.php @@ -0,0 +1,29 @@ +<?php + +namespace Stripe\Util; + +/** + * A very basic implementation of LoggerInterface that has just enough + * functionality that it can be the default for this library. + */ +class DefaultLogger implements LoggerInterface +{ + /** @var int */ + public $messageType = 0; + + /** @var null|string */ + public $destination; + + public function error($message, array $context = []) + { + if (\count($context) > 0) { + throw new \Stripe\Exception\BadMethodCallException('DefaultLogger does not currently implement context. Please implement if you need it.'); + } + + if (null === $this->destination) { + \error_log($message, $this->messageType); + } else { + \error_log($message, $this->messageType, $this->destination); + } + } +} diff --git a/vendor/stripe/stripe-php/lib/Util/LoggerInterface.php b/vendor/stripe/stripe-php/lib/Util/LoggerInterface.php new file mode 100644 index 0000000..5603c81 --- /dev/null +++ b/vendor/stripe/stripe-php/lib/Util/LoggerInterface.php @@ -0,0 +1,34 @@ +<?php + +namespace Stripe\Util; + +/** + * Describes a logger instance. + * + * This is a subset of the interface of the same name in the PSR-3 logger + * interface. We guarantee to keep it compatible, but we'd redefined it here so + * that we don't have to pull in the extra dependencies for users who don't want + * it. + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md + * for the full interface specification. + * + * The message MUST be a string or object implementing __toString(). + * + * The message MAY contain placeholders in the form: {foo} where foo + * will be replaced by the context data in key "foo". + * + * The context array can contain arbitrary data, the only assumption that + * can be made by implementors is that if an Exception instance is given + * to produce a stack trace, it MUST be in a key named "exception". + */ +interface LoggerInterface +{ + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + */ + public function error($message, array $context = []); +} diff --git a/vendor/stripe/stripe-php/lib/Util/ObjectTypes.php b/vendor/stripe/stripe-php/lib/Util/ObjectTypes.php new file mode 100644 index 0000000..6624760 --- /dev/null +++ b/vendor/stripe/stripe-php/lib/Util/ObjectTypes.php @@ -0,0 +1,103 @@ +<?php + +// File generated from our OpenAPI spec + +namespace Stripe\Util; + +class ObjectTypes +{ + /** + * @var array Mapping from object types to resource classes + */ + const mapping = [ + \Stripe\Account::OBJECT_NAME => \Stripe\Account::class, + \Stripe\AccountLink::OBJECT_NAME => \Stripe\AccountLink::class, + \Stripe\AlipayAccount::OBJECT_NAME => \Stripe\AlipayAccount::class, + \Stripe\ApplePayDomain::OBJECT_NAME => \Stripe\ApplePayDomain::class, + \Stripe\ApplicationFee::OBJECT_NAME => \Stripe\ApplicationFee::class, + \Stripe\ApplicationFeeRefund::OBJECT_NAME => \Stripe\ApplicationFeeRefund::class, + \Stripe\Balance::OBJECT_NAME => \Stripe\Balance::class, + \Stripe\BalanceTransaction::OBJECT_NAME => \Stripe\BalanceTransaction::class, + \Stripe\BankAccount::OBJECT_NAME => \Stripe\BankAccount::class, + \Stripe\BillingPortal\Configuration::OBJECT_NAME => \Stripe\BillingPortal\Configuration::class, + \Stripe\BillingPortal\Session::OBJECT_NAME => \Stripe\BillingPortal\Session::class, + \Stripe\BitcoinReceiver::OBJECT_NAME => \Stripe\BitcoinReceiver::class, + \Stripe\BitcoinTransaction::OBJECT_NAME => \Stripe\BitcoinTransaction::class, + \Stripe\Capability::OBJECT_NAME => \Stripe\Capability::class, + \Stripe\Card::OBJECT_NAME => \Stripe\Card::class, + \Stripe\Charge::OBJECT_NAME => \Stripe\Charge::class, + \Stripe\Checkout\Session::OBJECT_NAME => \Stripe\Checkout\Session::class, + \Stripe\Collection::OBJECT_NAME => \Stripe\Collection::class, + \Stripe\CountrySpec::OBJECT_NAME => \Stripe\CountrySpec::class, + \Stripe\Coupon::OBJECT_NAME => \Stripe\Coupon::class, + \Stripe\CreditNote::OBJECT_NAME => \Stripe\CreditNote::class, + \Stripe\CreditNoteLineItem::OBJECT_NAME => \Stripe\CreditNoteLineItem::class, + \Stripe\Customer::OBJECT_NAME => \Stripe\Customer::class, + \Stripe\CustomerBalanceTransaction::OBJECT_NAME => \Stripe\CustomerBalanceTransaction::class, + \Stripe\Discount::OBJECT_NAME => \Stripe\Discount::class, + \Stripe\Dispute::OBJECT_NAME => \Stripe\Dispute::class, + \Stripe\EphemeralKey::OBJECT_NAME => \Stripe\EphemeralKey::class, + \Stripe\Event::OBJECT_NAME => \Stripe\Event::class, + \Stripe\ExchangeRate::OBJECT_NAME => \Stripe\ExchangeRate::class, + \Stripe\File::OBJECT_NAME => \Stripe\File::class, + \Stripe\File::OBJECT_NAME_ALT => \Stripe\File::class, + \Stripe\FileLink::OBJECT_NAME => \Stripe\FileLink::class, + \Stripe\Identity\VerificationReport::OBJECT_NAME => \Stripe\Identity\VerificationReport::class, + \Stripe\Identity\VerificationSession::OBJECT_NAME => \Stripe\Identity\VerificationSession::class, + \Stripe\Invoice::OBJECT_NAME => \Stripe\Invoice::class, + \Stripe\InvoiceItem::OBJECT_NAME => \Stripe\InvoiceItem::class, + \Stripe\InvoiceLineItem::OBJECT_NAME => \Stripe\InvoiceLineItem::class, + \Stripe\Issuing\Authorization::OBJECT_NAME => \Stripe\Issuing\Authorization::class, + \Stripe\Issuing\Card::OBJECT_NAME => \Stripe\Issuing\Card::class, + \Stripe\Issuing\CardDetails::OBJECT_NAME => \Stripe\Issuing\CardDetails::class, + \Stripe\Issuing\Cardholder::OBJECT_NAME => \Stripe\Issuing\Cardholder::class, + \Stripe\Issuing\Dispute::OBJECT_NAME => \Stripe\Issuing\Dispute::class, + \Stripe\Issuing\Transaction::OBJECT_NAME => \Stripe\Issuing\Transaction::class, + \Stripe\LineItem::OBJECT_NAME => \Stripe\LineItem::class, + \Stripe\LoginLink::OBJECT_NAME => \Stripe\LoginLink::class, + \Stripe\Mandate::OBJECT_NAME => \Stripe\Mandate::class, + \Stripe\Order::OBJECT_NAME => \Stripe\Order::class, + \Stripe\OrderItem::OBJECT_NAME => \Stripe\OrderItem::class, + \Stripe\OrderReturn::OBJECT_NAME => \Stripe\OrderReturn::class, + \Stripe\PaymentIntent::OBJECT_NAME => \Stripe\PaymentIntent::class, + \Stripe\PaymentMethod::OBJECT_NAME => \Stripe\PaymentMethod::class, + \Stripe\Payout::OBJECT_NAME => \Stripe\Payout::class, + \Stripe\Person::OBJECT_NAME => \Stripe\Person::class, + \Stripe\Plan::OBJECT_NAME => \Stripe\Plan::class, + \Stripe\Price::OBJECT_NAME => \Stripe\Price::class, + \Stripe\Product::OBJECT_NAME => \Stripe\Product::class, + \Stripe\PromotionCode::OBJECT_NAME => \Stripe\PromotionCode::class, + \Stripe\Radar\EarlyFraudWarning::OBJECT_NAME => \Stripe\Radar\EarlyFraudWarning::class, + \Stripe\Radar\ValueList::OBJECT_NAME => \Stripe\Radar\ValueList::class, + \Stripe\Radar\ValueListItem::OBJECT_NAME => \Stripe\Radar\ValueListItem::class, + \Stripe\Recipient::OBJECT_NAME => \Stripe\Recipient::class, + \Stripe\RecipientTransfer::OBJECT_NAME => \Stripe\RecipientTransfer::class, + \Stripe\Refund::OBJECT_NAME => \Stripe\Refund::class, + \Stripe\Reporting\ReportRun::OBJECT_NAME => \Stripe\Reporting\ReportRun::class, + \Stripe\Reporting\ReportType::OBJECT_NAME => \Stripe\Reporting\ReportType::class, + \Stripe\Review::OBJECT_NAME => \Stripe\Review::class, + \Stripe\SetupAttempt::OBJECT_NAME => \Stripe\SetupAttempt::class, + \Stripe\SetupIntent::OBJECT_NAME => \Stripe\SetupIntent::class, + \Stripe\Sigma\ScheduledQueryRun::OBJECT_NAME => \Stripe\Sigma\ScheduledQueryRun::class, + \Stripe\SKU::OBJECT_NAME => \Stripe\SKU::class, + \Stripe\Source::OBJECT_NAME => \Stripe\Source::class, + \Stripe\SourceTransaction::OBJECT_NAME => \Stripe\SourceTransaction::class, + \Stripe\Subscription::OBJECT_NAME => \Stripe\Subscription::class, + \Stripe\SubscriptionItem::OBJECT_NAME => \Stripe\SubscriptionItem::class, + \Stripe\SubscriptionSchedule::OBJECT_NAME => \Stripe\SubscriptionSchedule::class, + \Stripe\TaxCode::OBJECT_NAME => \Stripe\TaxCode::class, + \Stripe\TaxId::OBJECT_NAME => \Stripe\TaxId::class, + \Stripe\TaxRate::OBJECT_NAME => \Stripe\TaxRate::class, + \Stripe\Terminal\ConnectionToken::OBJECT_NAME => \Stripe\Terminal\ConnectionToken::class, + \Stripe\Terminal\Location::OBJECT_NAME => \Stripe\Terminal\Location::class, + \Stripe\Terminal\Reader::OBJECT_NAME => \Stripe\Terminal\Reader::class, + \Stripe\ThreeDSecure::OBJECT_NAME => \Stripe\ThreeDSecure::class, + \Stripe\Token::OBJECT_NAME => \Stripe\Token::class, + \Stripe\Topup::OBJECT_NAME => \Stripe\Topup::class, + \Stripe\Transfer::OBJECT_NAME => \Stripe\Transfer::class, + \Stripe\TransferReversal::OBJECT_NAME => \Stripe\TransferReversal::class, + \Stripe\UsageRecord::OBJECT_NAME => \Stripe\UsageRecord::class, + \Stripe\UsageRecordSummary::OBJECT_NAME => \Stripe\UsageRecordSummary::class, + \Stripe\WebhookEndpoint::OBJECT_NAME => \Stripe\WebhookEndpoint::class, + ]; +} diff --git a/vendor/stripe/stripe-php/lib/Util/RandomGenerator.php b/vendor/stripe/stripe-php/lib/Util/RandomGenerator.php new file mode 100644 index 0000000..b11e445 --- /dev/null +++ b/vendor/stripe/stripe-php/lib/Util/RandomGenerator.php @@ -0,0 +1,36 @@ +<?php + +namespace Stripe\Util; + +/** + * A basic random generator. This is in a separate class so we the generator + * can be injected as a dependency and replaced with a mock in tests. + */ +class RandomGenerator +{ + /** + * Returns a random value between 0 and $max. + * + * @param float $max (optional) + * + * @return float + */ + public function randFloat($max = 1.0) + { + return \mt_rand() / \mt_getrandmax() * $max; + } + + /** + * Returns a v4 UUID. + * + * @return string + */ + public function uuid() + { + $arr = \array_values(\unpack('N1a/n4b/N1c', \openssl_random_pseudo_bytes(16))); + $arr[2] = ($arr[2] & 0x0fff) | 0x4000; + $arr[3] = ($arr[3] & 0x3fff) | 0x8000; + + return \vsprintf('%08x-%04x-%04x-%04x-%04x%08x', $arr); + } +} diff --git a/vendor/stripe/stripe-php/lib/Util/RequestOptions.php b/vendor/stripe/stripe-php/lib/Util/RequestOptions.php new file mode 100644 index 0000000..89924bb --- /dev/null +++ b/vendor/stripe/stripe-php/lib/Util/RequestOptions.php @@ -0,0 +1,168 @@ +<?php + +namespace Stripe\Util; + +class RequestOptions +{ + /** + * @var array<string> a list of headers that should be persisted across requests + */ + public static $HEADERS_TO_PERSIST = [ + 'Stripe-Account', + 'Stripe-Version', + ]; + + /** @var array<string, string> */ + public $headers; + + /** @var null|string */ + public $apiKey; + + /** @var null|string */ + public $apiBase; + + /** + * @param null|string $key + * @param array<string, string> $headers + * @param null|string $base + */ + public function __construct($key = null, $headers = [], $base = null) + { + $this->apiKey = $key; + $this->headers = $headers; + $this->apiBase = $base; + } + + /** + * @return array<string, string> + */ + public function __debugInfo() + { + return [ + 'apiKey' => $this->redactedApiKey(), + 'headers' => $this->headers, + 'apiBase' => $this->apiBase, + ]; + } + + /** + * Unpacks an options array and merges it into the existing RequestOptions + * object. + * + * @param null|array|RequestOptions|string $options a key => value array + * @param bool $strict when true, forbid string form and arbitrary keys in array form + * + * @return RequestOptions + */ + public function merge($options, $strict = false) + { + $other_options = self::parse($options, $strict); + if (null === $other_options->apiKey) { + $other_options->apiKey = $this->apiKey; + } + if (null === $other_options->apiBase) { + $other_options->apiBase = $this->apiBase; + } + $other_options->headers = \array_merge($this->headers, $other_options->headers); + + return $other_options; + } + + /** + * Discards all headers that we don't want to persist across requests. + */ + public function discardNonPersistentHeaders() + { + foreach ($this->headers as $k => $v) { + if (!\in_array($k, self::$HEADERS_TO_PERSIST, true)) { + unset($this->headers[$k]); + } + } + } + + /** + * Unpacks an options array into an RequestOptions object. + * + * @param null|array|RequestOptions|string $options a key => value array + * @param bool $strict when true, forbid string form and arbitrary keys in array form + * + * @throws \Stripe\Exception\InvalidArgumentException + * + * @return RequestOptions + */ + public static function parse($options, $strict = false) + { + if ($options instanceof self) { + return $options; + } + + if (null === $options) { + return new RequestOptions(null, [], null); + } + + if (\is_string($options)) { + if ($strict) { + $message = 'Do not pass a string for request options. If you want to set the ' + . 'API key, pass an array like ["api_key" => <apiKey>] instead.'; + + throw new \Stripe\Exception\InvalidArgumentException($message); + } + + return new RequestOptions($options, [], null); + } + + if (\is_array($options)) { + $headers = []; + $key = null; + $base = null; + + if (\array_key_exists('api_key', $options)) { + $key = $options['api_key']; + unset($options['api_key']); + } + if (\array_key_exists('idempotency_key', $options)) { + $headers['Idempotency-Key'] = $options['idempotency_key']; + unset($options['idempotency_key']); + } + if (\array_key_exists('stripe_account', $options)) { + $headers['Stripe-Account'] = $options['stripe_account']; + unset($options['stripe_account']); + } + if (\array_key_exists('stripe_version', $options)) { + $headers['Stripe-Version'] = $options['stripe_version']; + unset($options['stripe_version']); + } + if (\array_key_exists('api_base', $options)) { + $base = $options['api_base']; + unset($options['api_base']); + } + + if ($strict && !empty($options)) { + $message = 'Got unexpected keys in options array: ' . \implode(', ', \array_keys($options)); + + throw new \Stripe\Exception\InvalidArgumentException($message); + } + + return new RequestOptions($key, $headers, $base); + } + + $message = 'The second argument to Stripe API method calls is an ' + . 'optional per-request apiKey, which must be a string, or ' + . 'per-request options, which must be an array. (HINT: you can set ' + . 'a global apiKey by "Stripe::setApiKey(<apiKey>)")'; + + throw new \Stripe\Exception\InvalidArgumentException($message); + } + + private function redactedApiKey() + { + $pieces = \explode('_', $this->apiKey, 3); + $last = \array_pop($pieces); + $redactedLast = \strlen($last) > 4 + ? (\str_repeat('*', \strlen($last) - 4) . \substr($last, -4)) + : $last; + $pieces[] = $redactedLast; + + return \implode('_', $pieces); + } +} diff --git a/vendor/stripe/stripe-php/lib/Util/Set.php b/vendor/stripe/stripe-php/lib/Util/Set.php new file mode 100644 index 0000000..017f929 --- /dev/null +++ b/vendor/stripe/stripe-php/lib/Util/Set.php @@ -0,0 +1,44 @@ +<?php + +namespace Stripe\Util; + +use ArrayIterator; +use IteratorAggregate; + +class Set implements IteratorAggregate +{ + private $_elts; + + public function __construct($members = []) + { + $this->_elts = []; + foreach ($members as $item) { + $this->_elts[$item] = true; + } + } + + public function includes($elt) + { + return isset($this->_elts[$elt]); + } + + public function add($elt) + { + $this->_elts[$elt] = true; + } + + public function discard($elt) + { + unset($this->_elts[$elt]); + } + + public function toArray() + { + return \array_keys($this->_elts); + } + + public function getIterator() + { + return new ArrayIterator($this->toArray()); + } +} diff --git a/vendor/stripe/stripe-php/lib/Util/Util.php b/vendor/stripe/stripe-php/lib/Util/Util.php new file mode 100644 index 0000000..4f39050 --- /dev/null +++ b/vendor/stripe/stripe-php/lib/Util/Util.php @@ -0,0 +1,265 @@ +<?php + +namespace Stripe\Util; + +use Stripe\StripeObject; + +abstract class Util +{ + private static $isMbstringAvailable = null; + private static $isHashEqualsAvailable = null; + + /** + * Whether the provided array (or other) is a list rather than a dictionary. + * A list is defined as an array for which all the keys are consecutive + * integers starting at 0. Empty arrays are considered to be lists. + * + * @param array|mixed $array + * + * @return bool true if the given object is a list + */ + public static function isList($array) + { + if (!\is_array($array)) { + return false; + } + if ([] === $array) { + return true; + } + if (\array_keys($array) !== \range(0, \count($array) - 1)) { + return false; + } + + return true; + } + + /** + * Converts a response from the Stripe API to the corresponding PHP object. + * + * @param array $resp the response from the Stripe API + * @param array $opts + * + * @return array|StripeObject + */ + public static function convertToStripeObject($resp, $opts) + { + $types = \Stripe\Util\ObjectTypes::mapping; + if (self::isList($resp)) { + $mapped = []; + foreach ($resp as $i) { + $mapped[] = self::convertToStripeObject($i, $opts); + } + + return $mapped; + } + if (\is_array($resp)) { + if (isset($resp['object']) && \is_string($resp['object']) && isset($types[$resp['object']])) { + $class = $types[$resp['object']]; + } else { + $class = \Stripe\StripeObject::class; + } + + return $class::constructFrom($resp, $opts); + } + + return $resp; + } + + /** + * @param mixed|string $value a string to UTF8-encode + * + * @return mixed|string the UTF8-encoded string, or the object passed in if + * it wasn't a string + */ + public static function utf8($value) + { + if (null === self::$isMbstringAvailable) { + self::$isMbstringAvailable = \function_exists('mb_detect_encoding'); + + if (!self::$isMbstringAvailable) { + \trigger_error('It looks like the mbstring extension is not enabled. ' . + 'UTF-8 strings will not properly be encoded. Ask your system ' . + 'administrator to enable the mbstring extension, or write to ' . + 'support@stripe.com if you have any questions.', \E_USER_WARNING); + } + } + + if (\is_string($value) && self::$isMbstringAvailable && 'UTF-8' !== \mb_detect_encoding($value, 'UTF-8', true)) { + return \utf8_encode($value); + } + + return $value; + } + + /** + * Compares two strings for equality. The time taken is independent of the + * number of characters that match. + * + * @param string $a one of the strings to compare + * @param string $b the other string to compare + * + * @return bool true if the strings are equal, false otherwise + */ + public static function secureCompare($a, $b) + { + if (null === self::$isHashEqualsAvailable) { + self::$isHashEqualsAvailable = \function_exists('hash_equals'); + } + + if (self::$isHashEqualsAvailable) { + return \hash_equals($a, $b); + } + if (\strlen($a) !== \strlen($b)) { + return false; + } + + $result = 0; + for ($i = 0; $i < \strlen($a); ++$i) { + $result |= \ord($a[$i]) ^ \ord($b[$i]); + } + + return 0 === $result; + } + + /** + * Recursively goes through an array of parameters. If a parameter is an instance of + * ApiResource, then it is replaced by the resource's ID. + * Also clears out null values. + * + * @param mixed $h + * + * @return mixed + */ + public static function objectsToIds($h) + { + if ($h instanceof \Stripe\ApiResource) { + return $h->id; + } + if (static::isList($h)) { + $results = []; + foreach ($h as $v) { + $results[] = static::objectsToIds($v); + } + + return $results; + } + if (\is_array($h)) { + $results = []; + foreach ($h as $k => $v) { + if (null === $v) { + continue; + } + $results[$k] = static::objectsToIds($v); + } + + return $results; + } + + return $h; + } + + /** + * @param array $params + * + * @return string + */ + public static function encodeParameters($params) + { + $flattenedParams = self::flattenParams($params); + $pieces = []; + foreach ($flattenedParams as $param) { + list($k, $v) = $param; + $pieces[] = self::urlEncode($k) . '=' . self::urlEncode($v); + } + + return \implode('&', $pieces); + } + + /** + * @param array $params + * @param null|string $parentKey + * + * @return array + */ + public static function flattenParams($params, $parentKey = null) + { + $result = []; + + foreach ($params as $key => $value) { + $calculatedKey = $parentKey ? "{$parentKey}[{$key}]" : $key; + + if (self::isList($value)) { + $result = \array_merge($result, self::flattenParamsList($value, $calculatedKey)); + } elseif (\is_array($value)) { + $result = \array_merge($result, self::flattenParams($value, $calculatedKey)); + } else { + \array_push($result, [$calculatedKey, $value]); + } + } + + return $result; + } + + /** + * @param array $value + * @param string $calculatedKey + * + * @return array + */ + public static function flattenParamsList($value, $calculatedKey) + { + $result = []; + + foreach ($value as $i => $elem) { + if (self::isList($elem)) { + $result = \array_merge($result, self::flattenParamsList($elem, $calculatedKey)); + } elseif (\is_array($elem)) { + $result = \array_merge($result, self::flattenParams($elem, "{$calculatedKey}[{$i}]")); + } else { + \array_push($result, ["{$calculatedKey}[{$i}]", $elem]); + } + } + + return $result; + } + + /** + * @param string $key a string to URL-encode + * + * @return string the URL-encoded string + */ + public static function urlEncode($key) + { + $s = \urlencode((string) $key); + + // Don't use strict form encoding by changing the square bracket control + // characters back to their literals. This is fine by the server, and + // makes these parameter strings easier to read. + $s = \str_replace('%5B', '[', $s); + + return \str_replace('%5D', ']', $s); + } + + public static function normalizeId($id) + { + if (\is_array($id)) { + $params = $id; + $id = $params['id']; + unset($params['id']); + } else { + $params = []; + } + + return [$id, $params]; + } + + /** + * Returns UNIX timestamp in milliseconds. + * + * @return int current time in millis + */ + public static function currentTimeMillis() + { + return (int) \round(\microtime(true) * 1000); + } +} |