• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stripe / stripe-php / 11129599820

01 Oct 2024 04:33PM UTC coverage: 62.613% (-1.3%) from 63.944%
11129599820

push

github

web-flow
Support for APIs in the new API version 2024-09-30.acacia (#1756)

175 of 409 new or added lines in 26 files covered. (42.79%)

3 existing lines in 3 files now uncovered.

3547 of 5665 relevant lines covered (62.61%)

2.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.58
/lib/Util/Util.php
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
     *
19
     * @return bool true if the given object is a list
20
     */
21
    public static function isList($array)
6✔
22
    {
23
        if (!\is_array($array)) {
6✔
24
            return false;
5✔
25
        }
26
        if ([] === $array) {
6✔
27
            return true;
2✔
28
        }
29
        if (\array_keys($array) !== \range(0, \count($array) - 1)) {
6✔
30
            return false;
6✔
31
        }
32

33
        return true;
4✔
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|RequestOptions $opts
41
     * @param 'v1'|'v2'            $apiMode whether the response is from a v1 or v2 API
42
     *
43
     * @return array|StripeObject
44
     */
45
    public static function convertToStripeObject($resp, $opts, $apiMode = 'v1')
2✔
46
    {
47
        $types = 'v1' === $apiMode ? \Stripe\Util\ObjectTypes::mapping
2✔
NEW
48
            : \Stripe\Util\ObjectTypes::v2Mapping;
×
49
        if (self::isList($resp)) {
2✔
50
            $mapped = [];
×
51
            foreach ($resp as $i) {
×
NEW
52
                $mapped[] = self::convertToStripeObject($i, $opts, $apiMode);
×
53
            }
54

55
            return $mapped;
×
56
        }
57
        if (\is_array($resp)) {
2✔
58
            if (isset($resp['object']) && \is_string($resp['object'])
2✔
59
                && isset($types[$resp['object']])
2✔
60
            ) {
61
                $class = $types[$resp['object']];
2✔
62
                if ('v2' === $apiMode && ('v2.core.event' === $resp['object'])) {
2✔
NEW
63
                    $eventTypes = \Stripe\Util\EventTypes::thinEventMapping;
×
NEW
64
                    if (\array_key_exists('type', $resp) && \array_key_exists($resp['type'], $eventTypes)) {
×
NEW
65
                        $class = $eventTypes[$resp['type']];
×
66
                    } else {
NEW
67
                        $class = \Stripe\StripeObject::class;
×
68
                    }
69
                }
NEW
70
            } elseif (\array_key_exists('data', $resp) && \array_key_exists('next_page_url', $resp)) {
×
71
                // TODO: this is a horrible hack. The API needs
72
                // to return something for `object` here.
NEW
73
                $class = \Stripe\V2\Collection::class;
×
74
            } else {
75
                $class = \Stripe\StripeObject::class;
×
76
            }
77

78
            return $class::constructFrom($resp, $opts, $apiMode);
2✔
79
        }
80

81
        return $resp;
2✔
82
    }
83

84
    /**
85
     * @param mixed $json
86
     * @param mixed $class
87
     *
88
     * @throws \ReflectionException
89
     */
90
    public static function json_decode_thin_event_object($json, $class)
2✔
91
    {
92
        $reflection = new \ReflectionClass($class);
2✔
93
        $instance = $reflection->newInstanceWithoutConstructor();
2✔
94
        $json = json_decode($json, true);
2✔
95
        $properties = $reflection->getProperties();
2✔
96
        foreach ($properties as $key => $property) {
2✔
97
            if (\array_key_exists($property->getName(), $json)) {
2✔
98
                if ('related_object' === $property->getName()) {
2✔
99
                    $related_object = new \Stripe\RelatedObject();
1✔
100
                    $related_object->id = $json['related_object']['id'];
1✔
101
                    $related_object->url = $json['related_object']['url'];
1✔
102
                    $related_object->type = $json['related_object']['type'];
1✔
103
                    $property->setValue($instance, $related_object);
1✔
104
                } else {
105
                    $property->setAccessible(true);
2✔
106
                    $property->setValue($instance, $json[$property->getName()]);
2✔
107
                }
108
            }
109
        }
110

111
        return $instance;
2✔
112
    }
113

114
    /**
115
     * @param mixed|string $value a string to UTF8-encode
116
     *
117
     * @return mixed|string the UTF8-encoded string, or the object passed in if
118
     *    it wasn't a string
119
     */
120
    public static function utf8($value)
1✔
121
    {
122
        if (null === self::$isMbstringAvailable) {
1✔
NEW
123
            self::$isMbstringAvailable = \function_exists('mb_detect_encoding')
×
NEW
124
                && \function_exists('mb_convert_encoding');
×
125

126
            if (!self::$isMbstringAvailable) {
×
NEW
127
                \trigger_error(
×
NEW
128
                    'It looks like the mbstring extension is not enabled. ' .
×
NEW
129
                    'UTF-8 strings will not properly be encoded. Ask your system '
×
NEW
130
                    .
×
NEW
131
                    'administrator to enable the mbstring extension, or write to '
×
NEW
132
                    .
×
NEW
133
                    'support@stripe.com if you have any questions.',
×
NEW
134
                    \E_USER_WARNING
×
NEW
135
                );
×
136
            }
137
        }
138

139
        if (\is_string($value) && self::$isMbstringAvailable
1✔
140
            && 'UTF-8' !== \mb_detect_encoding($value, 'UTF-8', true)
1✔
141
        ) {
142
            return mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1');
1✔
143
        }
144

145
        return $value;
1✔
146
    }
147

148
    /**
149
     * Compares two strings for equality. The time taken is independent of the
150
     * number of characters that match.
151
     *
152
     * @param string $a one of the strings to compare
153
     * @param string $b the other string to compare
154
     *
155
     * @return bool true if the strings are equal, false otherwise
156
     */
157
    public static function secureCompare($a, $b)
×
158
    {
159
        if (null === self::$isHashEqualsAvailable) {
×
160
            self::$isHashEqualsAvailable = \function_exists('hash_equals');
×
161
        }
162

163
        if (self::$isHashEqualsAvailable) {
×
164
            return \hash_equals($a, $b);
×
165
        }
166
        if (\strlen($a) !== \strlen($b)) {
×
167
            return false;
×
168
        }
169

170
        $result = 0;
×
171
        for ($i = 0; $i < \strlen($a); ++$i) {
×
172
            $result |= \ord($a[$i]) ^ \ord($b[$i]);
×
173
        }
174

175
        return 0 === $result;
×
176
    }
177

178
    /**
179
     * Recursively goes through an array of parameters. If a parameter is an instance of
180
     * ApiResource, then it is replaced by the resource's ID.
181
     * Also clears out null values.
182
     *
183
     * @param mixed $h
184
     *
185
     * @return mixed
186
     */
187
    public static function objectsToIds($h)
1✔
188
    {
189
        if ($h instanceof \Stripe\ApiResource) {
1✔
190
            return $h->id;
1✔
191
        }
192
        if (static::isList($h)) {
1✔
193
            $results = [];
×
194
            foreach ($h as $v) {
×
195
                $results[] = static::objectsToIds($v);
×
196
            }
197

198
            return $results;
×
199
        }
200
        if (\is_array($h)) {
1✔
201
            $results = [];
1✔
202
            foreach ($h as $k => $v) {
1✔
203
                if (null === $v) {
1✔
204
                    continue;
1✔
205
                }
206
                $results[$k] = static::objectsToIds($v);
1✔
207
            }
208

209
            return $results;
1✔
210
        }
211

212
        return $h;
1✔
213
    }
214

215
    /**
216
     * @param array $params
217
     * @param mixed $apiMode
218
     *
219
     * @return string
220
     */
221
    public static function encodeParameters($params, $apiMode = 'v1')
2✔
222
    {
223
        $flattenedParams = self::flattenParams($params, null, $apiMode);
2✔
224
        $pieces = [];
2✔
225
        foreach ($flattenedParams as $param) {
2✔
226
            list($k, $v) = $param;
2✔
227
            $pieces[] = self::urlEncode($k) . '=' . self::urlEncode($v);
2✔
228
        }
229

230
        return \implode('&', $pieces);
2✔
231
    }
232

233
    /**
234
     * @param array       $params
235
     * @param null|string $parentKey
236
     * @param mixed       $apiMode
237
     *
238
     * @return array
239
     */
240
    public static function flattenParams(
3✔
241
        $params,
242
        $parentKey = null,
243
        $apiMode = 'v1'
244
    ) {
245
        $result = [];
3✔
246

247
        foreach ($params as $key => $value) {
3✔
248
            $calculatedKey = $parentKey ? "{$parentKey}[{$key}]" : $key;
3✔
249
            if (self::isList($value)) {
3✔
250
                $result = \array_merge(
3✔
251
                    $result,
3✔
252
                    self::flattenParamsList($value, $calculatedKey, $apiMode)
3✔
253
                );
3✔
254
            } elseif (\is_array($value)) {
3✔
255
                $result = \array_merge(
3✔
256
                    $result,
3✔
257
                    self::flattenParams($value, $calculatedKey, $apiMode)
3✔
258
                );
3✔
259
            } else {
260
                \array_push($result, [$calculatedKey, $value]);
3✔
261
            }
262
        }
263

264
        return $result;
3✔
265
    }
266

267
    /**
268
     * @param array  $value
269
     * @param string $calculatedKey
270
     * @param mixed  $apiMode
271
     *
272
     * @return array
273
     */
274
    public static function flattenParamsList(
3✔
275
        $value,
276
        $calculatedKey,
277
        $apiMode = 'v1'
278
    ) {
279
        $result = [];
3✔
280

281
        foreach ($value as $i => $elem) {
3✔
282
            if (self::isList($elem)) {
3✔
NEW
283
                $result = \array_merge(
×
NEW
284
                    $result,
×
NEW
285
                    self::flattenParamsList($elem, $calculatedKey)
×
NEW
286
                );
×
287
            } elseif (\is_array($elem)) {
3✔
288
                $result = \array_merge(
1✔
289
                    $result,
1✔
290
                    self::flattenParams($elem, "{$calculatedKey}[{$i}]")
1✔
291
                );
1✔
292
            } else {
293
                if ('v2' === $apiMode) {
3✔
294
                    \array_push($result, ["{$calculatedKey}", $elem]);
1✔
295
                } else {
296
                    \array_push($result, ["{$calculatedKey}[{$i}]", $elem]);
2✔
297
                }
298
            }
299
        }
300

301
        return $result;
3✔
302
    }
303

304
    /**
305
     * @param string $key a string to URL-encode
306
     *
307
     * @return string the URL-encoded string
308
     */
309
    public static function urlEncode($key)
3✔
310
    {
311
        $s = \urlencode((string) $key);
3✔
312

313
        // Don't use strict form encoding by changing the square bracket control
314
        // characters back to their literals. This is fine by the server, and
315
        // makes these parameter strings easier to read.
316
        $s = \str_replace('%5B', '[', $s);
3✔
317

318
        return \str_replace('%5D', ']', $s);
3✔
319
    }
320

321
    public static function normalizeId($id)
2✔
322
    {
323
        if (\is_array($id)) {
2✔
324
            // see https://github.com/stripe/stripe-php/pull/1602
325
            if (!isset($id['id'])) {
×
326
                return [null, $id];
×
327
            }
328
            $params = $id;
×
329
            $id = $params['id'];
×
330
            unset($params['id']);
×
331
        } else {
332
            $params = [];
2✔
333
        }
334

335
        return [$id, $params];
2✔
336
    }
337

338
    /**
339
     * Returns UNIX timestamp in milliseconds.
340
     *
341
     * @return int current time in millis
342
     */
343
    public static function currentTimeMillis()
×
344
    {
345
        return (int) \round(\microtime(true) * 1000);
×
346
    }
347

NEW
348
    public static function getApiMode($path)
×
349
    {
NEW
350
        $apiMode = 'v1';
×
NEW
351
        if ('/v2' === substr($path, 0, 3)) {
×
NEW
352
            $apiMode = 'v2';
×
353
        }
354

NEW
355
        return $apiMode;
×
356
    }
357
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc