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

stripe / stripe-php / 11168146605

03 Oct 2024 07:07PM UTC coverage: 58.299% (-1.0%) from 59.255%
11168146605

push

github

web-flow
Merge updates from stripe-php master to beta (#1766)

* added new example template and instructions on how to create more

* Revert "added new example template and instructions on how to create more"

This reverts commit 002550f73.

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

* Removed parseSnapshotEvent (#1764)

* Bump version to 16.0.0

* Updated the class names

* Fixed PHPcsfixer

---------

Co-authored-by: Jesse Rosalia <jar@stripe.com>
Co-authored-by: Ramya Rao <100975018+ramya-stripe@users.noreply.github.com>
Co-authored-by: prathmesh-stripe <165320323+prathmesh-stripe@users.noreply.github.com>
Co-authored-by: Ramya Rao <ramya@stripe.com>
Co-authored-by: Prathmesh Ranaut <prathmesh@stripe.com>

137 of 344 new or added lines in 26 files covered. (39.83%)

4 existing lines in 4 files now uncovered.

3558 of 6103 relevant lines covered (58.3%)

2.29 hits per line

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

64.74
/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)
3✔
91
    {
92
        $reflection = new \ReflectionClass($class);
3✔
93
        $instance = $reflection->newInstanceWithoutConstructor();
3✔
94
        $json = json_decode($json, true);
3✔
95
        $properties = $reflection->getProperties();
3✔
96
        foreach ($properties as $key => $property) {
3✔
97
            if (\array_key_exists($property->getName(), $json)) {
3✔
98
                if ('related_object' === $property->getName()) {
3✔
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
                } elseif ('reason' === $property->getName()) {
3✔
105
                    $reason = new \Stripe\Reason();
1✔
106
                    $reason->id = $json['reason']['id'];
1✔
107
                    $reason->idempotency_key = $json['reason']['idempotency_key'];
1✔
108
                    $property->setValue($instance, $reason);
1✔
109
                } else {
110
                    $property->setAccessible(true);
3✔
111
                    $property->setValue($instance, $json[$property->getName()]);
3✔
112
                }
113
            }
114
        }
115

116
        return $instance;
3✔
117
    }
118

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

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

144
        if (\is_string($value) && self::$isMbstringAvailable
1✔
145
            && 'UTF-8' !== \mb_detect_encoding($value, 'UTF-8', true)
1✔
146
        ) {
147
            return mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1');
1✔
148
        }
149

150
        return $value;
1✔
151
    }
152

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

168
        if (self::$isHashEqualsAvailable) {
×
169
            return \hash_equals($a, $b);
×
170
        }
171
        if (\strlen($a) !== \strlen($b)) {
×
172
            return false;
×
173
        }
174

175
        $result = 0;
×
176
        for ($i = 0; $i < \strlen($a); ++$i) {
×
177
            $result |= \ord($a[$i]) ^ \ord($b[$i]);
×
178
        }
179

180
        return 0 === $result;
×
181
    }
182

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

203
            return $results;
×
204
        }
205
        if (\is_array($h)) {
1✔
206
            $results = [];
1✔
207
            foreach ($h as $k => $v) {
1✔
208
                if (null === $v) {
1✔
209
                    continue;
1✔
210
                }
211
                $results[$k] = static::objectsToIds($v);
1✔
212
            }
213

214
            return $results;
1✔
215
        }
216

217
        return $h;
1✔
218
    }
219

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

235
        return \implode('&', $pieces);
2✔
236
    }
237

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

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

269
        return $result;
3✔
270
    }
271

272
    /**
273
     * @param array  $value
274
     * @param string $calculatedKey
275
     * @param mixed  $apiMode
276
     *
277
     * @return array
278
     */
279
    public static function flattenParamsList(
3✔
280
        $value,
281
        $calculatedKey,
282
        $apiMode = 'v1'
283
    ) {
284
        $result = [];
3✔
285

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

306
        return $result;
3✔
307
    }
308

309
    /**
310
     * @param string $key a string to URL-encode
311
     *
312
     * @return string the URL-encoded string
313
     */
314
    public static function urlEncode($key)
3✔
315
    {
316
        $s = \urlencode((string) $key);
3✔
317

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

323
        return \str_replace('%5D', ']', $s);
3✔
324
    }
325

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

340
        return [$id, $params];
2✔
341
    }
342

343
    /**
344
     * Returns UNIX timestamp in milliseconds.
345
     *
346
     * @return int current time in millis
347
     */
348
    public static function currentTimeMillis()
×
349
    {
350
        return (int) \round(\microtime(true) * 1000);
×
351
    }
352

NEW
353
    public static function getApiMode($path)
×
354
    {
NEW
355
        $apiMode = 'v1';
×
NEW
356
        if ('/v2' === substr($path, 0, 3)) {
×
NEW
357
            $apiMode = 'v2';
×
358
        }
359

NEW
360
        return $apiMode;
×
361
    }
362
}
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