· 5 years ago · Jul 24, 2020, 06:18 PM
1<?php
2
3namespace Stripe\Util;
4
5use Stripe\StripeObject;
6
7abstract class Util
8{
9 private static $isMbstringAvailable = null;
10 private static $isHashEqualsAvailable = null;
11
12 /**
13 * Whether the provided array (or other) is a list rather than a dictionary.
14 * A list is defined as an array for which all the keys are consecutive
15 * integers starting at 0. Empty arrays are considered to be lists.
16 *
17 * @param array|mixed $array
18 *
19 * @return bool true if the given object is a list
20 */
21 public static function isList($array)
22 {
23 if (!\is_array($array)) {
24 return false;
25 }
26 if ($array === []) {
27 return true;
28 }
29 if (\array_keys($array) !== \range(0, \count($array) - 1)) {
30 return false;
31 }
32
33 return true;
34 }
35
36 /**
37 * Converts a response from the Stripe API to the corresponding PHP object.
38 *
39 * @param array $resp the response from the Stripe API
40 * @param array $opts
41 *
42 * @return array|StripeObject
43 */
44 public static function convertToStripeObject($resp, $opts)
45 {
46 $types = \Stripe\Util\ObjectTypes::mapping;
47 if (self::isList($resp)) {
48 $mapped = [];
49 foreach ($resp as $i) {
50 \array_push($mapped, self::convertToStripeObject($i, $opts));
51 }
52
53 return $mapped;
54 }
55 if (\is_array($resp)) {
56 if (isset($resp['object']) && \is_string($resp['object']) && isset($types[$resp['object']])) {
57 $class = $types[$resp['object']];
58 } else {
59 $class = \Stripe\StripeObject::class;
60 }
61
62 return $class::constructFrom($resp, $opts);
63 }
64
65 return $resp;
66 }
67
68 /**
69 * @param mixed|string $value a string to UTF8-encode
70 *
71 * @return mixed|string the UTF8-encoded string, or the object passed in if
72 * it wasn't a string
73 */
74 public static function utf8($value)
75 {
76 if (null === self::$isMbstringAvailable) {
77 self::$isMbstringAvailable = \function_exists('mb_detect_encoding');
78
79 if (!self::$isMbstringAvailable) {
80 \trigger_error('It looks like the mbstring extension is not enabled. ' .
81 'UTF-8 strings will not properly be encoded. Ask your system ' .
82 'administrator to enable the mbstring extension, or write to ' .
83 'support@stripe.com if you have any questions.', \E_USER_WARNING);
84 }
85 }
86
87 if (\is_string($value) && self::$isMbstringAvailable && 'UTF-8' !== \mb_detect_encoding($value, 'UTF-8', true)) {
88 return \utf8_encode($value);
89 }
90
91 return $value;
92 }
93
94 /**
95 * Compares two strings for equality. The time taken is independent of the
96 * number of characters that match.
97 *
98 * @param string $a one of the strings to compare
99 * @param string $b the other string to compare
100 *
101 * @return bool true if the strings are equal, false otherwise
102 */
103 public static function secureCompare($a, $b)
104 {
105 if (null === self::$isHashEqualsAvailable) {
106 self::$isHashEqualsAvailable = \function_exists('hash_equals');
107 }
108
109 if (self::$isHashEqualsAvailable) {
110 return \hash_equals($a, $b);
111 }
112 if (\strlen($a) !== \strlen($b)) {
113 return false;
114 }
115
116 $result = 0;
117 for ($i = 0; $i < \strlen($a); ++$i) {
118 $result |= \ord($a[$i]) ^ \ord($b[$i]);
119 }
120
121 return 0 === $result;
122 }
123
124 /**
125 * Recursively goes through an array of parameters. If a parameter is an instance of
126 * ApiResource, then it is replaced by the resource's ID.
127 * Also clears out null values.
128 *
129 * @param mixed $h
130 *
131 * @return mixed
132 */
133 public static function objectsToIds($h)
134 {
135 if ($h instanceof \Stripe\ApiResource) {
136 return $h->id;
137 }
138 if (static::isList($h)) {
139 $results = [];
140 foreach ($h as $v) {
141 \array_push($results, static::objectsToIds($v));
142 }
143
144 return $results;
145 }
146 if (\is_array($h)) {
147 $results = [];
148 foreach ($h as $k => $v) {
149 if (null === $v) {
150 continue;
151 }
152 $results[$k] = static::objectsToIds($v);
153 }
154
155 return $results;
156 }
157
158 return $h;
159 }
160
161 /**
162 * @param array $params
163 *
164 * @return string
165 */
166 public static function encodeParameters($params)
167 {
168 $flattenedParams = self::flattenParams($params);
169 $pieces = [];
170 foreach ($flattenedParams as $param) {
171 list($k, $v) = $param;
172 \array_push($pieces, self::urlEncode($k) . '=' . self::urlEncode($v));
173 }
174
175 return \implode('&', $pieces);
176 }
177
178 /**
179 * @param array $params
180 * @param null|string $parentKey
181 *
182 * @return array
183 */
184 public static function flattenParams($params, $parentKey = null)
185 {
186 $result = [];
187
188 foreach ($params as $key => $value) {
189 $calculatedKey = $parentKey ? "{$parentKey}[{$key}]" : $key;
190
191 if (self::isList($value)) {
192 $result = \array_merge($result, self::flattenParamsList($value, $calculatedKey));
193 } elseif (\is_array($value)) {
194 $result = \array_merge($result, self::flattenParams($value, $calculatedKey));
195 } else {
196 \array_push($result, [$calculatedKey, $value]);
197 }
198 }
199
200 return $result;
201 }
202
203 /**
204 * @param array $value
205 * @param string $calculatedKey
206 *
207 * @return array
208 */
209 public static function flattenParamsList($value, $calculatedKey)
210 {
211 $result = [];
212
213 foreach ($value as $i => $elem) {
214 if (self::isList($elem)) {
215 $result = \array_merge($result, self::flattenParamsList($elem, $calculatedKey));
216 } elseif (\is_array($elem)) {
217 $result = \array_merge($result, self::flattenParams($elem, "{$calculatedKey}[{$i}]"));
218 } else {
219 \array_push($result, ["{$calculatedKey}[{$i}]", $elem]);
220 }
221 }
222
223 return $result;
224 }
225
226 /**
227 * @param string $key a string to URL-encode
228 *
229 * @return string the URL-encoded string
230 */
231 public static function urlEncode($key)
232 {
233 $s = \urlencode((string) $key);
234
235 // Don't use strict form encoding by changing the square bracket control
236 // characters back to their literals. This is fine by the server, and
237 // makes these parameter strings easier to read.
238 $s = \str_replace('%5B', '[', $s);
239
240 return \str_replace('%5D', ']', $s);
241 }
242
243 public static function normalizeId($id)
244 {
245 if (\is_array($id)) {
246 $params = $id;
247 $id = $params['id'];
248 unset($params['id']);
249 } else {
250 $params = [];
251 }
252
253 return [$id, $params];
254 }
255
256 /**
257 * Returns UNIX timestamp in milliseconds.
258 *
259 * @return int current time in millis
260 */
261 public static function currentTimeMillis()
262 {
263 return (int) \round(\microtime(true) * 1000);
264 }
265}
266