Test Failed
Push — master ( 3dd85e...34f16b )
by Devin
04:34 queued 10s
created

Util::objectsToIds()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 7
nop 1
dl 0
loc 23
rs 8.6186
c 0
b 0
f 0
1
<?php
2
3
namespace Stripe\Util;
4
5
use Stripe\StripeObject;
6
7
abstract 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
     * @return boolean true if the given object is a list.
19
     */
20
    public static function isList($array)
21
    {
22
        if (!is_array($array)) {
23
            return false;
24
        }
25
        if ($array === []) {
26
            return true;
27
        }
28
        if (array_keys($array) !== range(0, count($array) - 1)) {
29
            return false;
30
        }
31
        return true;
32
    }
33
34
    /**
35
     * Recursively converts the PHP Stripe object to an array.
36
     *
37
     * @param array $values The PHP Stripe object to convert.
38
     * @return array
39
     */
40
    public static function convertStripeObjectToArray($values)
41
    {
42
        $results = [];
43
        foreach ($values as $k => $v) {
44
            // FIXME: this is an encapsulation violation
45
            if ($k[0] == '_') {
46
                continue;
47
            }
48
            if ($v instanceof StripeObject) {
49
                $results[$k] = $v->__toArray(true);
50
            } elseif (is_array($v)) {
51
                $results[$k] = self::convertStripeObjectToArray($v);
52
            } else {
53
                $results[$k] = $v;
54
            }
55
        }
56
        return $results;
57
    }
58
59
    /**
60
     * Converts a response from the Stripe API to the corresponding PHP object.
61
     *
62
     * @param array $resp The response from the Stripe API.
63
     * @param array $opts
64
     * @return StripeObject|array
65
     */
66
    public static function convertToStripeObject($resp, $opts)
67
    {
68
        $types = [
69
            // data structures
70
            \Stripe\Collection::OBJECT_NAME => 'Stripe\\Collection',
71
72
            // business objects
73
            \Stripe\Account::OBJECT_NAME => 'Stripe\\Account',
74
            \Stripe\AccountLink::OBJECT_NAME => 'Stripe\\AccountLink',
75
            \Stripe\AlipayAccount::OBJECT_NAME => 'Stripe\\AlipayAccount',
76
            \Stripe\ApplePayDomain::OBJECT_NAME => 'Stripe\\ApplePayDomain',
77
            \Stripe\ApplicationFee::OBJECT_NAME => 'Stripe\\ApplicationFee',
78
            \Stripe\Balance::OBJECT_NAME => 'Stripe\\Balance',
79
            \Stripe\BalanceTransaction::OBJECT_NAME => 'Stripe\\BalanceTransaction',
80
            \Stripe\BankAccount::OBJECT_NAME => 'Stripe\\BankAccount',
81
            \Stripe\BitcoinReceiver::OBJECT_NAME => 'Stripe\\BitcoinReceiver',
82
            \Stripe\BitcoinTransaction::OBJECT_NAME => 'Stripe\\BitcoinTransaction',
83
            \Stripe\Capability::OBJECT_NAME => 'Stripe\\Capability',
84
            \Stripe\Card::OBJECT_NAME => 'Stripe\\Card',
85
            \Stripe\Charge::OBJECT_NAME => 'Stripe\\Charge',
86
            \Stripe\Checkout\Session::OBJECT_NAME => 'Stripe\\Checkout\\Session',
87
            \Stripe\CountrySpec::OBJECT_NAME => 'Stripe\\CountrySpec',
88
            \Stripe\Coupon::OBJECT_NAME => 'Stripe\\Coupon',
89
            \Stripe\CreditNote::OBJECT_NAME => 'Stripe\\CreditNote',
90
            \Stripe\Customer::OBJECT_NAME => 'Stripe\\Customer',
91
            \Stripe\CustomerBalanceTransaction::OBJECT_NAME => 'Stripe\\CustomerBalanceTransaction',
92
            \Stripe\Discount::OBJECT_NAME => 'Stripe\\Discount',
93
            \Stripe\Dispute::OBJECT_NAME => 'Stripe\\Dispute',
94
            \Stripe\EphemeralKey::OBJECT_NAME => 'Stripe\\EphemeralKey',
95
            \Stripe\Event::OBJECT_NAME => 'Stripe\\Event',
96
            \Stripe\ExchangeRate::OBJECT_NAME => 'Stripe\\ExchangeRate',
97
            \Stripe\ApplicationFeeRefund::OBJECT_NAME => 'Stripe\\ApplicationFeeRefund',
98
            \Stripe\File::OBJECT_NAME => 'Stripe\\File',
99
            \Stripe\File::OBJECT_NAME_ALT => 'Stripe\\File',
100
            \Stripe\FileLink::OBJECT_NAME => 'Stripe\\FileLink',
101
            \Stripe\Invoice::OBJECT_NAME => 'Stripe\\Invoice',
102
            \Stripe\InvoiceItem::OBJECT_NAME => 'Stripe\\InvoiceItem',
103
            \Stripe\InvoiceLineItem::OBJECT_NAME => 'Stripe\\InvoiceLineItem',
104
            \Stripe\IssuerFraudRecord::OBJECT_NAME => 'Stripe\\IssuerFraudRecord',
105
            \Stripe\Issuing\Authorization::OBJECT_NAME => 'Stripe\\Issuing\\Authorization',
106
            \Stripe\Issuing\Card::OBJECT_NAME => 'Stripe\\Issuing\\Card',
107
            \Stripe\Issuing\CardDetails::OBJECT_NAME => 'Stripe\\Issuing\\CardDetails',
108
            \Stripe\Issuing\Cardholder::OBJECT_NAME => 'Stripe\\Issuing\\Cardholder',
109
            \Stripe\Issuing\Dispute::OBJECT_NAME => 'Stripe\\Issuing\\Dispute',
110
            \Stripe\Issuing\Transaction::OBJECT_NAME => 'Stripe\\Issuing\\Transaction',
111
            \Stripe\LoginLink::OBJECT_NAME => 'Stripe\\LoginLink',
112
            \Stripe\Order::OBJECT_NAME => 'Stripe\\Order',
113
            \Stripe\OrderItem::OBJECT_NAME => 'Stripe\\OrderItem',
114
            \Stripe\OrderReturn::OBJECT_NAME => 'Stripe\\OrderReturn',
115
            \Stripe\PaymentIntent::OBJECT_NAME => 'Stripe\\PaymentIntent',
116
            \Stripe\PaymentMethod::OBJECT_NAME => 'Stripe\\PaymentMethod',
117
            \Stripe\Payout::OBJECT_NAME => 'Stripe\\Payout',
118
            \Stripe\Person::OBJECT_NAME => 'Stripe\\Person',
119
            \Stripe\Plan::OBJECT_NAME => 'Stripe\\Plan',
120
            \Stripe\Product::OBJECT_NAME => 'Stripe\\Product',
121
            \Stripe\Radar\EarlyFraudWarning::OBJECT_NAME => 'Stripe\\Radar\\EarlyFraudWarning',
122
            \Stripe\Radar\ValueList::OBJECT_NAME => 'Stripe\\Radar\\ValueList',
123
            \Stripe\Radar\ValueListItem::OBJECT_NAME => 'Stripe\\Radar\\ValueListItem',
124
            \Stripe\Recipient::OBJECT_NAME => 'Stripe\\Recipient',
125
            \Stripe\RecipientTransfer::OBJECT_NAME => 'Stripe\\RecipientTransfer',
126
            \Stripe\Refund::OBJECT_NAME => 'Stripe\\Refund',
127
            \Stripe\Reporting\ReportRun::OBJECT_NAME => 'Stripe\\Reporting\\ReportRun',
128
            \Stripe\Reporting\ReportType::OBJECT_NAME => 'Stripe\\Reporting\\ReportType',
129
            \Stripe\Review::OBJECT_NAME => 'Stripe\\Review',
130
            \Stripe\SetupIntent::OBJECT_NAME => 'Stripe\\SetupIntent',
131
            \Stripe\SKU::OBJECT_NAME => 'Stripe\\SKU',
132
            \Stripe\Sigma\ScheduledQueryRun::OBJECT_NAME => 'Stripe\\Sigma\\ScheduledQueryRun',
133
            \Stripe\Source::OBJECT_NAME => 'Stripe\\Source',
134
            \Stripe\SourceTransaction::OBJECT_NAME => 'Stripe\\SourceTransaction',
135
            \Stripe\Subscription::OBJECT_NAME => 'Stripe\\Subscription',
136
            \Stripe\SubscriptionItem::OBJECT_NAME => 'Stripe\\SubscriptionItem',
137
            \Stripe\SubscriptionSchedule::OBJECT_NAME => 'Stripe\\SubscriptionSchedule',
138
            \Stripe\SubscriptionScheduleRevision::OBJECT_NAME => 'Stripe\\SubscriptionScheduleRevision',
139
            \Stripe\TaxId::OBJECT_NAME => 'Stripe\\TaxId',
140
            \Stripe\TaxRate::OBJECT_NAME => 'Stripe\\TaxRate',
141
            \Stripe\ThreeDSecure::OBJECT_NAME => 'Stripe\\ThreeDSecure',
142
            \Stripe\Terminal\ConnectionToken::OBJECT_NAME => 'Stripe\\Terminal\\ConnectionToken',
143
            \Stripe\Terminal\Location::OBJECT_NAME => 'Stripe\\Terminal\\Location',
144
            \Stripe\Terminal\Reader::OBJECT_NAME => 'Stripe\\Terminal\\Reader',
145
            \Stripe\Token::OBJECT_NAME => 'Stripe\\Token',
146
            \Stripe\Topup::OBJECT_NAME => 'Stripe\\Topup',
147
            \Stripe\Transfer::OBJECT_NAME => 'Stripe\\Transfer',
148
            \Stripe\TransferReversal::OBJECT_NAME => 'Stripe\\TransferReversal',
149
            \Stripe\UsageRecord::OBJECT_NAME => 'Stripe\\UsageRecord',
150
            \Stripe\UsageRecordSummary::OBJECT_NAME => 'Stripe\\UsageRecordSummary',
151
            \Stripe\WebhookEndpoint::OBJECT_NAME => 'Stripe\\WebhookEndpoint',
152
        ];
153
        if (self::isList($resp)) {
154
            $mapped = [];
155
            foreach ($resp as $i) {
156
                array_push($mapped, self::convertToStripeObject($i, $opts));
157
            }
158
            return $mapped;
159
        } elseif (is_array($resp)) {
160
            if (isset($resp['object']) && is_string($resp['object']) && isset($types[$resp['object']])) {
161
                $class = $types[$resp['object']];
162
            } else {
163
                $class = 'Stripe\\StripeObject';
164
            }
165
            return $class::constructFrom($resp, $opts);
166
        } else {
167
            return $resp;
168
        }
169
    }
170
171
    /**
172
     * @param string|mixed $value A string to UTF8-encode.
173
     *
174
     * @return string|mixed The UTF8-encoded string, or the object passed in if
175
     *    it wasn't a string.
176
     */
177
    public static function utf8($value)
178
    {
179
        if (self::$isMbstringAvailable === null) {
180
            self::$isMbstringAvailable = function_exists('mb_detect_encoding');
181
182
            if (!self::$isMbstringAvailable) {
183
                trigger_error("It looks like the mbstring extension is not enabled. " .
184
                    "UTF-8 strings will not properly be encoded. Ask your system " .
185
                    "administrator to enable the mbstring extension, or write to " .
186
                    "[email protected] if you have any questions.", E_USER_WARNING);
187
            }
188
        }
189
190
        if (is_string($value) && self::$isMbstringAvailable && mb_detect_encoding($value, "UTF-8", true) != "UTF-8") {
191
            return utf8_encode($value);
192
        } else {
193
            return $value;
194
        }
195
    }
196
197
    /**
198
     * Compares two strings for equality. The time taken is independent of the
199
     * number of characters that match.
200
     *
201
     * @param string $a one of the strings to compare.
202
     * @param string $b the other string to compare.
203
     * @return bool true if the strings are equal, false otherwise.
204
     */
205
    public static function secureCompare($a, $b)
206
    {
207
        if (self::$isHashEqualsAvailable === null) {
208
            self::$isHashEqualsAvailable = function_exists('hash_equals');
209
        }
210
211
        if (self::$isHashEqualsAvailable) {
212
            return hash_equals($a, $b);
213
        } else {
214
            if (strlen($a) != strlen($b)) {
215
                return false;
216
            }
217
218
            $result = 0;
219
            for ($i = 0; $i < strlen($a); $i++) {
220
                $result |= ord($a[$i]) ^ ord($b[$i]);
221
            }
222
            return ($result == 0);
223
        }
224
    }
225
226
    /**
227
     * Recursively goes through an array of parameters. If a parameter is an instance of
228
     * ApiResource, then it is replaced by the resource's ID.
229
     * Also clears out null values.
230
     *
231
     * @param mixed $h
232
     * @return mixed
233
     */
234
    public static function objectsToIds($h)
235
    {
236
        if ($h instanceof \Stripe\ApiResource) {
237
            return $h->id;
238
        } elseif (static::isList($h)) {
239
            $results = [];
240
            foreach ($h as $v) {
241
                array_push($results, static::objectsToIds($v));
242
            }
243
            return $results;
244
        } elseif (is_array($h)) {
245
            $results = [];
246
            foreach ($h as $k => $v) {
247
                if (is_null($v)) {
248
                    continue;
249
                }
250
                $results[$k] = static::objectsToIds($v);
251
            }
252
            return $results;
253
        } else {
254
            return $h;
255
        }
256
    }
257
258
    /**
259
     * @param array $params
260
     *
261
     * @return string
262
     */
263
    public static function encodeParameters($params)
264
    {
265
        $flattenedParams = self::flattenParams($params);
266
        $pieces = [];
267
        foreach ($flattenedParams as $param) {
268
            list($k, $v) = $param;
269
            array_push($pieces, self::urlEncode($k) . '=' . self::urlEncode($v));
270
        }
271
        return implode('&', $pieces);
272
    }
273
274
    /**
275
     * @param array $params
276
     * @param string|null $parentKey
277
     *
278
     * @return array
279
     */
280
    public static function flattenParams($params, $parentKey = null)
281
    {
282
        $result = [];
283
284
        foreach ($params as $key => $value) {
285
            $calculatedKey = $parentKey ? "{$parentKey}[{$key}]" : $key;
286
287 View Code Duplication
            if (self::isList($value)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
288
                $result = array_merge($result, self::flattenParamsList($value, $calculatedKey));
289
            } elseif (is_array($value)) {
290
                $result = array_merge($result, self::flattenParams($value, $calculatedKey));
291
            } else {
292
                array_push($result, [$calculatedKey, $value]);
293
            }
294
        }
295
296
        return $result;
297
    }
298
299
    /**
300
     * @param array $value
301
     * @param string $calculatedKey
302
     *
303
     * @return array
304
     */
305
    public static function flattenParamsList($value, $calculatedKey)
306
    {
307
        $result = [];
308
309
        foreach ($value as $i => $elem) {
310 View Code Duplication
            if (self::isList($elem)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311
                $result = array_merge($result, self::flattenParamsList($elem, $calculatedKey));
312
            } elseif (is_array($elem)) {
313
                $result = array_merge($result, self::flattenParams($elem, "{$calculatedKey}[{$i}]"));
314
            } else {
315
                array_push($result, ["{$calculatedKey}[{$i}]", $elem]);
316
            }
317
        }
318
319
        return $result;
320
    }
321
322
    /**
323
     * @param string $key A string to URL-encode.
324
     *
325
     * @return string The URL-encoded string.
326
     */
327
    public static function urlEncode($key)
328
    {
329
        $s = urlencode($key);
330
331
        // Don't use strict form encoding by changing the square bracket control
332
        // characters back to their literals. This is fine by the server, and
333
        // makes these parameter strings easier to read.
334
        $s = str_replace('%5B', '[', $s);
335
        $s = str_replace('%5D', ']', $s);
336
337
        return $s;
338
    }
339
340
    public static function normalizeId($id)
341
    {
342
        if (is_array($id)) {
343
            $params = $id;
344
            $id = $params['id'];
345
            unset($params['id']);
346
        } else {
347
            $params = [];
348
        }
349
        return [$id, $params];
350
    }
351
352
    /**
353
     * Returns UNIX timestamp in milliseconds
354
     *
355
     * @return integer current time in millis
356
     */
357
    public static function currentTimeMillis()
358
    {
359
        return (int) round(microtime(true) * 1000);
360
    }
361
}
362