1
|
|
|
<?php |
2
|
|
|
namespace Braintree; |
3
|
|
|
|
4
|
|
|
use DateTime; |
5
|
|
|
use InvalidArgumentException; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Braintree Utility methods |
9
|
|
|
* PHP version 5 |
10
|
|
|
* |
11
|
|
|
* @copyright 2015 Braintree, a division of PayPal, Inc. |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
class Util |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* extracts an attribute and returns an array of objects |
18
|
|
|
* |
19
|
|
|
* extracts the requested element from an array, and converts the contents |
20
|
|
|
* of its child arrays to objects of type $attributeName, or returns |
21
|
|
|
* an array with a single element containing the value of that array element |
22
|
|
|
* |
23
|
|
|
* @param array $attribArray attributes from a search response |
24
|
|
|
* @param string $attributeName indicates which element of the passed array to extract |
25
|
|
|
* @return array array of $attributeName objects, or a single element array |
26
|
|
|
*/ |
27
|
|
|
public static function extractAttributeAsArray(&$attribArray, $attributeName) |
28
|
|
|
{ |
29
|
|
|
if(!isset($attribArray[$attributeName])): |
30
|
|
|
return []; |
31
|
|
|
endif; |
32
|
|
|
|
33
|
|
|
// get what should be an array from the passed array |
34
|
|
|
$data = $attribArray[$attributeName]; |
35
|
|
|
// set up the class that will be used to convert each array element |
36
|
|
|
$classFactory = self::buildClassName($attributeName) . '::factory'; |
37
|
|
|
if(is_array($data)): |
38
|
|
|
// create an object from the data in each element |
39
|
|
|
$objectArray = array_map($classFactory, $data); |
40
|
|
|
else: |
41
|
|
|
return [$data]; |
42
|
|
|
endif; |
43
|
|
|
|
44
|
|
|
unset($attribArray[$attributeName]); |
45
|
|
|
return $objectArray; |
46
|
|
|
} |
47
|
|
|
/** |
48
|
|
|
* throws an exception based on the type of error |
49
|
|
|
* @param string $statusCode HTTP status code to throw exception from |
50
|
|
|
* @param null|string $message |
51
|
|
|
* @throws Exception multiple types depending on the error |
52
|
|
|
* @return void |
53
|
|
|
*/ |
54
|
|
|
public static function throwStatusCodeException($statusCode, $message=null) |
55
|
|
|
{ |
56
|
|
|
switch($statusCode) { |
57
|
|
|
case 401: |
58
|
|
|
throw new Exception\Authentication(); |
59
|
|
|
break; |
|
|
|
|
60
|
|
|
case 403: |
61
|
|
|
throw new Exception\Authorization($message); |
62
|
|
|
break; |
|
|
|
|
63
|
|
|
case 404: |
64
|
|
|
throw new Exception\NotFound(); |
65
|
|
|
break; |
|
|
|
|
66
|
|
|
case 426: |
67
|
|
|
throw new Exception\UpgradeRequired(); |
68
|
|
|
break; |
|
|
|
|
69
|
|
|
case 429: |
70
|
|
|
throw new Exception\TooManyRequests(); |
71
|
|
|
break; |
|
|
|
|
72
|
|
|
case 500: |
73
|
|
|
throw new Exception\ServerError(); |
74
|
|
|
break; |
|
|
|
|
75
|
|
|
case 503: |
76
|
|
|
throw new Exception\DownForMaintenance(); |
77
|
|
|
break; |
|
|
|
|
78
|
|
|
default: |
79
|
|
|
throw new Exception\Unexpected('Unexpected HTTP_RESPONSE #' . $statusCode); |
80
|
|
|
break; |
|
|
|
|
81
|
|
|
} |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* |
86
|
|
|
* @param string $className |
87
|
|
|
* @param object $resultObj |
88
|
|
|
* @return object returns the passed object if successful |
89
|
|
|
* @throws Exception\ValidationsFailed |
90
|
|
|
*/ |
91
|
|
|
public static function returnObjectOrThrowException($className, $resultObj) |
92
|
|
|
{ |
93
|
|
|
$resultObjName = self::cleanClassName($className); |
94
|
|
|
if ($resultObj->success) { |
95
|
|
|
return $resultObj->$resultObjName; |
96
|
|
|
} else { |
97
|
|
|
throw new Exception\ValidationsFailed(); |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* removes the header from a classname |
103
|
|
|
* |
104
|
|
|
* @param string $name ClassName |
105
|
|
|
* @return camelCased classname minus header |
106
|
|
|
*/ |
107
|
|
|
public static function cleanClassName($name) |
108
|
|
|
{ |
109
|
|
|
$classNamesToResponseKeys = [ |
110
|
|
|
'Braintree\CreditCard' => 'creditCard', |
111
|
|
|
'Braintree_CreditCard' => 'creditCard', |
112
|
|
|
'Braintree\CreditCardGateway' => 'creditCard', |
113
|
|
|
'Braintree_CreditCardGateway' => 'creditCard', |
114
|
|
|
'Braintree\Customer' => 'customer', |
115
|
|
|
'Braintree_Customer' => 'customer', |
116
|
|
|
'Braintree\CustomerGateway' => 'customer', |
117
|
|
|
'Braintree_CustomerGateway' => 'customer', |
118
|
|
|
'Braintree\Subscription' => 'subscription', |
119
|
|
|
'Braintree_Subscription' => 'subscription', |
120
|
|
|
'Braintree\SubscriptionGateway' => 'subscription', |
121
|
|
|
'Braintree_SubscriptionGateway' => 'subscription', |
122
|
|
|
'Braintree\Transaction' => 'transaction', |
123
|
|
|
'Braintree_Transaction' => 'transaction', |
124
|
|
|
'Braintree\TransactionGateway' => 'transaction', |
125
|
|
|
'Braintree_TransactionGateway' => 'transaction', |
126
|
|
|
'Braintree\CreditCardVerification' => 'verification', |
127
|
|
|
'Braintree_CreditCardVerification' => 'verification', |
128
|
|
|
'Braintree\CreditCardVerificationGateway' => 'verification', |
129
|
|
|
'Braintree_CreditCardVerificationGateway' => 'verification', |
130
|
|
|
'Braintree\AddOn' => 'addOn', |
131
|
|
|
'Braintree_AddOn' => 'addOn', |
132
|
|
|
'Braintree\AddOnGateway' => 'addOn', |
133
|
|
|
'Braintree_AddOnGateway' => 'addOn', |
134
|
|
|
'Braintree\Discount' => 'discount', |
135
|
|
|
'Braintree_Discount' => 'discount', |
136
|
|
|
'Braintree\DiscountGateway' => 'discount', |
137
|
|
|
'Braintree_DiscountGateway' => 'discount', |
138
|
|
|
'Braintree\Plan' => 'plan', |
139
|
|
|
'Braintree_Plan' => 'plan', |
140
|
|
|
'Braintree\PlanGateway' => 'plan', |
141
|
|
|
'Braintree_PlanGateway' => 'plan', |
142
|
|
|
'Braintree\Address' => 'address', |
143
|
|
|
'Braintree_Address' => 'address', |
144
|
|
|
'Braintree\AddressGateway' => 'address', |
145
|
|
|
'Braintree_AddressGateway' => 'address', |
146
|
|
|
'Braintree\SettlementBatchSummary' => 'settlementBatchSummary', |
147
|
|
|
'Braintree_SettlementBatchSummary' => 'settlementBatchSummary', |
148
|
|
|
'Braintree\SettlementBatchSummaryGateway' => 'settlementBatchSummary', |
149
|
|
|
'Braintree_SettlementBatchSummaryGateway' => 'settlementBatchSummary', |
150
|
|
|
'Braintree\Merchant' => 'merchant', |
151
|
|
|
'Braintree_Merchant' => 'merchant', |
152
|
|
|
'Braintree\MerchantGateway' => 'merchant', |
153
|
|
|
'Braintree_MerchantGateway' => 'merchant', |
154
|
|
|
'Braintree\MerchantAccount' => 'merchantAccount', |
155
|
|
|
'Braintree_MerchantAccount' => 'merchantAccount', |
156
|
|
|
'Braintree\MerchantAccountGateway' => 'merchantAccount', |
157
|
|
|
'Braintree_MerchantAccountGateway' => 'merchantAccount', |
158
|
|
|
'Braintree\OAuthCredentials' => 'credentials', |
159
|
|
|
'Braintree_OAuthCredentials' => 'credentials', |
160
|
|
|
'Braintree\OAuthResult' => 'result', |
161
|
|
|
'Braintree_OAuthResult' => 'result', |
162
|
|
|
'Braintree\PayPalAccount' => 'paypalAccount', |
163
|
|
|
'Braintree_PayPalAccount' => 'paypalAccount', |
164
|
|
|
'Braintree\PayPalAccountGateway' => 'paypalAccount', |
165
|
|
|
'Braintree_PayPalAccountGateway' => 'paypalAccount', |
166
|
|
|
]; |
167
|
|
|
|
168
|
|
|
return $classNamesToResponseKeys[$name]; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* |
173
|
|
|
* @param string $name className |
174
|
|
|
* @return string ClassName |
175
|
|
|
*/ |
176
|
|
|
public static function buildClassName($name) |
177
|
|
|
{ |
178
|
|
|
$responseKeysToClassNames = [ |
179
|
|
|
'creditCard' => 'Braintree\CreditCard', |
180
|
|
|
'customer' => 'Braintree\Customer', |
181
|
|
|
'subscription' => 'Braintree\Subscription', |
182
|
|
|
'transaction' => 'Braintree\Transaction', |
183
|
|
|
'verification' => 'Braintree\CreditCardVerification', |
184
|
|
|
'addOn' => 'Braintree\AddOn', |
185
|
|
|
'discount' => 'Braintree\Discount', |
186
|
|
|
'plan' => 'Braintree\Plan', |
187
|
|
|
'address' => 'Braintree\Address', |
188
|
|
|
'settlementBatchSummary' => 'Braintree\SettlementBatchSummary', |
189
|
|
|
'merchantAccount' => 'Braintree\MerchantAccount', |
190
|
|
|
]; |
191
|
|
|
|
192
|
|
|
return (string) $responseKeysToClassNames[$name]; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* convert alpha-beta-gamma to alphaBetaGamma |
197
|
|
|
* |
198
|
|
|
* @access public |
199
|
|
|
* @param string $string |
200
|
|
|
* @param null|string $delimiter |
201
|
|
|
* @return string modified string |
202
|
|
|
*/ |
203
|
|
|
public static function delimiterToCamelCase($string, $delimiter = '[\-\_]') |
204
|
|
|
{ |
205
|
|
|
// php doesn't garbage collect functions created by create_function() |
206
|
|
|
// so use a static variable to avoid adding a new function to memory |
207
|
|
|
// every time this function is called. |
208
|
|
|
static $callback = null; |
209
|
|
|
if ($callback === null) { |
210
|
|
|
$callback = create_function('$matches', 'return strtoupper($matches[1]);'); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
return preg_replace_callback('/' . $delimiter . '(\w)/', $callback, $string); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* convert alpha-beta-gamma to alpha_beta_gamma |
218
|
|
|
* |
219
|
|
|
* @access public |
220
|
|
|
* @param string $string |
221
|
|
|
* @return string modified string |
222
|
|
|
*/ |
223
|
|
|
public static function delimiterToUnderscore($string) |
224
|
|
|
{ |
225
|
|
|
return preg_replace('/-/', '_', $string); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* find capitals and convert to delimiter + lowercase |
231
|
|
|
* |
232
|
|
|
* @access public |
233
|
|
|
* @param string $string |
234
|
|
|
* @param null|string $delimiter |
235
|
|
|
* @return string modified string |
236
|
|
|
*/ |
237
|
|
|
public static function camelCaseToDelimiter($string, $delimiter = '-') |
238
|
|
|
{ |
239
|
|
|
return strtolower(preg_replace('/([A-Z])/', "$delimiter\\1", $string)); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
public static function delimiterToCamelCaseArray($array, $delimiter = '[\-\_]') |
243
|
|
|
{ |
244
|
|
|
$converted = []; |
245
|
|
|
foreach ($array as $key => $value) { |
246
|
|
|
if (is_string($key)) { |
247
|
|
|
$key = self::delimiterToCamelCase($key, $delimiter); |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
if (is_array($value)) { |
251
|
|
|
// Make an exception for custom fields, which must be underscore (can't be |
252
|
|
|
// camelCase). |
253
|
|
|
if ($key === 'customFields') { |
254
|
|
|
$value = self::delimiterToUnderscoreArray($value); |
255
|
|
|
} else { |
256
|
|
|
$value = self::delimiterToCamelCaseArray($value, $delimiter); |
257
|
|
|
} |
258
|
|
|
} |
259
|
|
|
$converted[$key] = $value; |
260
|
|
|
} |
261
|
|
|
return $converted; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
public static function camelCaseToDelimiterArray($array, $delimiter = '-') |
265
|
|
|
{ |
266
|
|
|
$converted = []; |
267
|
|
|
foreach ($array as $key => $value) { |
268
|
|
|
if (is_string($key)) { |
269
|
|
|
$key = self::camelCaseToDelimiter($key, $delimiter); |
270
|
|
|
} |
271
|
|
|
if (is_array($value)) { |
272
|
|
|
$value = self::camelCaseToDelimiterArray($value, $delimiter); |
273
|
|
|
} |
274
|
|
|
$converted[$key] = $value; |
275
|
|
|
} |
276
|
|
|
return $converted; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
public static function delimiterToUnderscoreArray($array) |
280
|
|
|
{ |
281
|
|
|
$converted = []; |
282
|
|
|
foreach ($array as $key => $value) { |
283
|
|
|
$key = self::delimiterToUnderscore($key); |
284
|
|
|
$converted[$key] = $value; |
285
|
|
|
} |
286
|
|
|
return $converted; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
/** |
290
|
|
|
* |
291
|
|
|
* @param array $array associative array to implode |
292
|
|
|
* @param string $separator (optional, defaults to =) |
293
|
|
|
* @param string $glue (optional, defaults to ', ') |
294
|
|
|
* @return bool |
295
|
|
|
*/ |
296
|
|
|
public static function implodeAssociativeArray($array, $separator = '=', $glue = ', ') |
297
|
|
|
{ |
298
|
|
|
// build a new array with joined keys and values |
299
|
|
|
$tmpArray = null; |
300
|
|
|
foreach ($array AS $key => $value) { |
301
|
|
|
if ($value instanceof DateTime) { |
302
|
|
|
$value = $value->format('r'); |
303
|
|
|
} |
304
|
|
|
$tmpArray[] = $key . $separator . $value; |
305
|
|
|
} |
306
|
|
|
// implode and return the new array |
307
|
|
|
return (is_array($tmpArray)) ? implode($glue, $tmpArray) : false; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
public static function attributesToString($attributes) { |
311
|
|
|
$printableAttribs = []; |
312
|
|
|
foreach ($attributes AS $key => $value) { |
313
|
|
|
if (is_array($value)) { |
314
|
|
|
$pAttrib = self::attributesToString($value); |
315
|
|
|
} else if ($value instanceof DateTime) { |
316
|
|
|
$pAttrib = $value->format(DateTime::RFC850); |
317
|
|
|
} else { |
318
|
|
|
$pAttrib = $value; |
319
|
|
|
} |
320
|
|
|
$printableAttribs[$key] = sprintf('%s', $pAttrib); |
321
|
|
|
} |
322
|
|
|
return self::implodeAssociativeArray($printableAttribs); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* verify user request structure |
327
|
|
|
* |
328
|
|
|
* compares the expected signature of a gateway request |
329
|
|
|
* against the actual structure sent by the user |
330
|
|
|
* |
331
|
|
|
* @param array $signature |
332
|
|
|
* @param array $attributes |
333
|
|
|
*/ |
334
|
|
|
public static function verifyKeys($signature, $attributes) |
335
|
|
|
{ |
336
|
|
|
$validKeys = self::_flattenArray($signature); |
337
|
|
|
$userKeys = self::_flattenUserKeys($attributes); |
338
|
|
|
$invalidKeys = array_diff($userKeys, $validKeys); |
339
|
|
|
$invalidKeys = self::_removeWildcardKeys($validKeys, $invalidKeys); |
340
|
|
|
|
341
|
|
|
if(!empty($invalidKeys)) { |
342
|
|
|
asort($invalidKeys); |
343
|
|
|
$sortedList = join(', ', $invalidKeys); |
344
|
|
|
throw new InvalidArgumentException('invalid keys: ' . $sortedList); |
345
|
|
|
} |
346
|
|
|
} |
347
|
|
|
/** |
348
|
|
|
* flattens a numerically indexed nested array to a single level |
349
|
|
|
* @param array $keys |
350
|
|
|
* @param string $namespace |
351
|
|
|
* @return array |
352
|
|
|
*/ |
353
|
|
|
private static function _flattenArray($keys, $namespace = null) |
354
|
|
|
{ |
355
|
|
|
$flattenedArray = []; |
356
|
|
|
foreach($keys AS $key) { |
357
|
|
|
if(is_array($key)) { |
358
|
|
|
$theKeys = array_keys($key); |
359
|
|
|
$theValues = array_values($key); |
360
|
|
|
$scope = $theKeys[0]; |
361
|
|
|
$fullKey = empty($namespace) ? $scope : $namespace . '[' . $scope . ']'; |
362
|
|
|
$flattenedArray = array_merge($flattenedArray, self::_flattenArray($theValues[0], $fullKey)); |
363
|
|
|
} else { |
364
|
|
|
$fullKey = empty($namespace) ? $key : $namespace . '[' . $key . ']'; |
365
|
|
|
$flattenedArray[] = $fullKey; |
366
|
|
|
} |
367
|
|
|
} |
368
|
|
|
sort($flattenedArray); |
369
|
|
|
return $flattenedArray; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
private static function _flattenUserKeys($keys, $namespace = null) |
373
|
|
|
{ |
374
|
|
|
$flattenedArray = []; |
375
|
|
|
|
376
|
|
|
foreach($keys AS $key => $value) { |
377
|
|
|
$fullKey = empty($namespace) ? $key : $namespace; |
378
|
|
|
if (!is_numeric($key) && $namespace != null) { |
379
|
|
|
$fullKey .= '[' . $key . ']'; |
380
|
|
|
} |
381
|
|
|
if (is_numeric($key) && is_string($value)) { |
382
|
|
|
$fullKey .= '[' . $value . ']'; |
383
|
|
|
} |
384
|
|
|
if(is_array($value)) { |
385
|
|
|
$more = self::_flattenUserKeys($value, $fullKey); |
386
|
|
|
$flattenedArray = array_merge($flattenedArray, $more); |
387
|
|
|
} else { |
388
|
|
|
$flattenedArray[] = $fullKey; |
389
|
|
|
} |
390
|
|
|
} |
391
|
|
|
sort($flattenedArray); |
392
|
|
|
return $flattenedArray; |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* removes wildcard entries from the invalid keys array |
397
|
|
|
* @param array $validKeys |
398
|
|
|
* @param <array $invalidKeys |
|
|
|
|
399
|
|
|
* @return array |
400
|
|
|
*/ |
401
|
|
|
private static function _removeWildcardKeys($validKeys, $invalidKeys) |
402
|
|
|
{ |
403
|
|
|
foreach($validKeys AS $key) { |
404
|
|
|
if (stristr($key, '[_anyKey_]')) { |
405
|
|
|
$wildcardKey = str_replace('[_anyKey_]', '', $key); |
406
|
|
|
foreach ($invalidKeys AS $index => $invalidKey) { |
407
|
|
|
if (stristr($invalidKey, $wildcardKey)) { |
408
|
|
|
unset($invalidKeys[$index]); |
409
|
|
|
} |
410
|
|
|
} |
411
|
|
|
} |
412
|
|
|
} |
413
|
|
|
return $invalidKeys; |
414
|
|
|
} |
415
|
|
|
} |
416
|
|
|
class_alias('Braintree\Util', 'Braintree_Util'); |
417
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.