Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 422fc66137 | |||
| 308794488d | |||
| 2bcd42f4a5 | |||
| 05b33ac157 |
11
Readme.md
11
Readme.md
@@ -1,9 +1,11 @@
|
||||
# Amazon Incentives - Gift Codes on Demand stand alone class
|
||||
|
||||
This is a abastract from (https://github.com/kamerk22/AmazonGiftCode) to be not dependend on Laravel base code.
|
||||
This is a abastract from [https://github.com/kamerk22/AmazonGiftCod](https://github.com/kamerk22/AmazonGiftCode) to be not dependend on Laravel base code.
|
||||
|
||||
Amazon Gift Codes On Demand (AGCOD). Integration for Amazon Incentive API.
|
||||
|
||||
[General Amazon Incentives Documentation](https://developer.amazon.com/docs/incentives-api/digital-gift-cards.html)
|
||||
|
||||
## How to install
|
||||
|
||||
`composer require gullevek/amazon-incentives`
|
||||
@@ -82,10 +84,13 @@ The error code is the curl handler error code.
|
||||
The error message is json encoded array with the layout
|
||||
|
||||
Use
|
||||
|
||||
```php
|
||||
$exception_array = gullevek\AmazonIncentives\AmazonIncentives::decodeExceptionMessage($exception_message);
|
||||
```
|
||||
|
||||
to extract the below array from the thrown exception
|
||||
|
||||
```php
|
||||
[
|
||||
'status' => 'AWS Status FAILURE or RESEND',
|
||||
@@ -106,7 +111,7 @@ to extract the below array from the thrown exception
|
||||
if code is T001 then this is a request flood error:
|
||||
In this case the request has to be resend after a certain waiting period.
|
||||
|
||||
### E9999
|
||||
### E999
|
||||
|
||||
if code is E999 some other critical error has happened
|
||||
|
||||
@@ -117,6 +122,7 @@ if code is E001 if the return create/cancel/check calls is not an array
|
||||
### C001
|
||||
|
||||
fif code is C001 curl failed to init
|
||||
|
||||
### C002
|
||||
|
||||
if code is C002 a curl error has happened
|
||||
@@ -132,6 +138,7 @@ If AWS_DEBUG is set to 1 and internal array will be written with debug info.
|
||||
The gulleek\AmazonIncentives\Debug\AmazonDebug class handles all this.
|
||||
|
||||
In the gulleek\AmazonIncentives\AmazonIncentives main class the debugger gets set
|
||||
|
||||
* setDebug that turns debugger on/off and if on sets unique id (getId to check)
|
||||
|
||||
New entries can be written with
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
"gullevek/dotenv": "dev-master"
|
||||
"gullevek/dotenv": "dev-master",
|
||||
"dg/bypass-finals": "dev-master"
|
||||
}
|
||||
}
|
||||
|
||||
59
composer.lock
generated
59
composer.lock
generated
@@ -4,9 +4,63 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "412ca7fe7d62707c222169f4a7deeaec",
|
||||
"content-hash": "717f24a1578a5d25a0522cfb908b13cc",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "dg/bypass-finals",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dg/bypass-finals.git",
|
||||
"reference": "fb62dc6ab1a097e234fa1567943d8e87ea4d0842"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dg/bypass-finals/zipball/fb62dc6ab1a097e234fa1567943d8e87ea4d0842",
|
||||
"reference": "fb62dc6ab1a097e234fa1567943d8e87ea4d0842",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.3",
|
||||
"phpstan/phpstan": "^0.12"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause",
|
||||
"GPL-2.0",
|
||||
"GPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
}
|
||||
],
|
||||
"description": "Removes final keyword from source code on-the-fly and allows mocking of final methods and classes",
|
||||
"keywords": [
|
||||
"finals",
|
||||
"mocking",
|
||||
"phpunit",
|
||||
"testing",
|
||||
"unit"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/dg/bypass-finals/issues",
|
||||
"source": "https://github.com/dg/bypass-finals/tree/master"
|
||||
},
|
||||
"time": "2022-04-14T12:24:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
"version": "1.5.x-dev",
|
||||
@@ -2090,7 +2144,8 @@
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {
|
||||
"gullevek/dotenv": 20
|
||||
"gullevek/dotenv": 20,
|
||||
"dg/bypass-finals": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
|
||||
@@ -2,4 +2,8 @@
|
||||
colors="true"
|
||||
verbose="true"
|
||||
>
|
||||
<!-- Below removes final from classes for mock tests -->
|
||||
<extensions>
|
||||
<extension class="test\phpUnit\Hook\BypassFinalHook" file="test/phpUnit/Hook/BypassFinalHook.php" />
|
||||
</extensions>
|
||||
</phpunit>
|
||||
|
||||
@@ -137,22 +137,12 @@ final class AmazonIncentives
|
||||
*
|
||||
* @param string $message Exception message json string
|
||||
* @return array<mixed> Decoded with code, type, message fields
|
||||
*
|
||||
* @deprecated use \gullevek\AmazonIncentives\Exceptions\AmazonErrors::decodeExceptionMessage()
|
||||
*/
|
||||
public static function decodeExceptionMessage(string $message): array
|
||||
{
|
||||
$message_ar = json_decode($message, true);
|
||||
// if we have an error, build empty block and only fill message
|
||||
if (json_last_error()) {
|
||||
$message_ar = [
|
||||
'status' => '',
|
||||
'code' => '',
|
||||
'type' => '',
|
||||
'message' => $message,
|
||||
'log_id' => '',
|
||||
'log' => []
|
||||
];
|
||||
}
|
||||
return $message_ar;
|
||||
return AmazonErrors::decodeExceptionMessage($message);
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
|
||||
@@ -39,6 +39,31 @@ final class AmazonErrors extends RuntimeException
|
||||
$_error_code
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the Exception message body
|
||||
* Returns an array with code (Amazon error codes), type (Amazon error info)
|
||||
* message (Amazon returned error message string)
|
||||
*
|
||||
* @param string $message Exception message json string
|
||||
* @return array<mixed> Decoded with code, type, message fields
|
||||
*/
|
||||
public static function decodeExceptionMessage(string $message): array
|
||||
{
|
||||
$message_ar = json_decode($message, true);
|
||||
// if we have an error, build empty block and only fill message
|
||||
if (json_last_error()) {
|
||||
$message_ar = [
|
||||
'status' => '',
|
||||
'code' => '',
|
||||
'type' => '',
|
||||
'message' => $message,
|
||||
'log_id' => '',
|
||||
'log' => []
|
||||
];
|
||||
}
|
||||
return $message_ar;
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
|
||||
140
test/agcod_http_return_samples.md
Normal file
140
test/agcod_http_return_samples.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Return data from AWS as json string
|
||||
|
||||
## Mock tests self
|
||||
|
||||
Tests run in local mock tests without connection to AWS
|
||||
|
||||
### funds
|
||||
|
||||
```json
|
||||
{"availableFunds":{"amount":0.0,"currencyCode":"JPY"},"status":"SUCCESS","timestamp":"20220610T085450Z"}
|
||||
```
|
||||
|
||||
### buy
|
||||
|
||||
```json
|
||||
{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a309167e7a4","gcClaimCode":"LJ49-AKDUV6-UYCP","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5535125272070255","status":"SUCCESS"}
|
||||
```
|
||||
|
||||
### cancel
|
||||
|
||||
```json
|
||||
{"creationRequestId":"EG3bd_62a309167e7a4","gcId":"5535125272070255","status":"SUCCESS"}
|
||||
```
|
||||
|
||||
### buy other 1
|
||||
|
||||
```json
|
||||
{"cardInfo":{"cardNumber":null,"cardStatus":"RefundedToPurchaser","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a309167e7a4","gcClaimCode":"LJ49-AKDUV6-UYCP","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5535125272070255","status":"SUCCESS"}
|
||||
```
|
||||
|
||||
### buy aother 2
|
||||
|
||||
```json
|
||||
{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a30923e9705","gcClaimCode":"UM97-FD5QKK-WKCT","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5540334324324221","status":"SUCCESS"}
|
||||
```
|
||||
|
||||
### buy other 2 (same create request id)
|
||||
|
||||
```json
|
||||
{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a30923e9705","gcClaimCode":"UM97-FD5QKK-WKCT","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5540334324324221","status":"SUCCESS"}
|
||||
```
|
||||
|
||||
## AWS GCOD Mocks
|
||||
|
||||
Mocking on AWS side, these will be returned if given request ID codes are sent.
|
||||
A working test account must exist for this
|
||||
|
||||
## success
|
||||
|
||||
### buy gift card F0000
|
||||
|
||||
```json
|
||||
{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":500.0,"currencyCode":"JPY"}},"creationRequestId":"F0000","gcClaimCode":"ZYXW-VUTS-RQPO","gcExpirationDate":null,"gcId":"ABC123ZYX987","status":"SUCCESS"}
|
||||
```
|
||||
|
||||
## errors
|
||||
|
||||
### F1000 -> F100
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F100","errorType":"GeneralError","message":"General Error"}
|
||||
```
|
||||
|
||||
### F2003 -> F200
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"InvalidAmountInput","message":"Amount can't be null"}
|
||||
```
|
||||
|
||||
### F2004 -> F200
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"InvalidAmountValue","message":"Amount must be larger than 0"}
|
||||
```
|
||||
|
||||
### F2005 -> F200
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"InvalidCurrencyCodeInput","message":"Currency Code can't be null or empty"}
|
||||
```
|
||||
|
||||
### F2010 -> F200
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"CardAlreadyActivatedWithDifferentRequestId","message":"The card was already activated with a different request id"}
|
||||
```
|
||||
|
||||
### F2015 -> F200
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"MaxAmountExceeded","message":"Max Amount Exceeded"}
|
||||
```
|
||||
|
||||
### F2016 -> F200
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"CurrencyCodeMismatch","message":"Currency Code Mismatch"}
|
||||
```
|
||||
|
||||
### F2017 -> F200
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"FractionalAmountNotAllowed","message":"Fractional Amount Not Allowed"}
|
||||
```
|
||||
|
||||
### F2047 -> F200
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"CancelRequestArrivedAfterTimeLimit","message":"Cancellation cannot be processed as too much time has elapsed since creation"}
|
||||
```
|
||||
|
||||
### F3003 -> F300
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F300","errorType":"InsufficientFunds","message":"Insufficient Funds"}
|
||||
```
|
||||
|
||||
### F3005 -> F300
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F300","errorType":"GeneralError","message":"General Error"}
|
||||
```
|
||||
|
||||
### F3010 -> F300
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F300","errorType":"CustomerSurpassedDailyVelocityLimit","message":"Customer has exceeded daily velocity limit"}
|
||||
```
|
||||
|
||||
### F4000 -> F400
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"RESEND"},"errorCode":"F400","errorType":"SystemTemporarilyUnavailable","message":"System Temporarily Unavailable"}
|
||||
```
|
||||
|
||||
### F5000 -> F500
|
||||
|
||||
```json
|
||||
{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F500","errorType":"GeneralError","message":"General Error"}
|
||||
```
|
||||
@@ -59,6 +59,7 @@ $loader->addPsr4('gullevek\\', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_
|
||||
// print "LOADER: <pre>" . print_r($loader, true) . "</pre>";
|
||||
|
||||
use gullevek\AmazonIncentives\AmazonIncentives;
|
||||
use gullevek\AmazonIncentives\Exceptions\AmazonErrors;
|
||||
use gullevek\dotEnv\DotEnv;
|
||||
|
||||
// load env data with dotenv
|
||||
@@ -75,30 +76,34 @@ print "<h1>Amazon Gift Card Incentives</h1><br>";
|
||||
// optional
|
||||
// debug: AWS_DEBUG (if not set: off)
|
||||
|
||||
// run info test (prints ENV vars)
|
||||
$run_info_test = !empty($_GET['info']) ? true : false;
|
||||
// run test to get funds info
|
||||
$run_fund_test = !empty($_GET['fund']) ? true : false;
|
||||
// run the normal get/cancel gift card tests
|
||||
$run_gift_tests = !empty($_GET['gift']) ? true : false;
|
||||
// run mock error check tests
|
||||
$run_mocks = !empty($_GET['mocks']) ? true : false;
|
||||
|
||||
// should we print debug info
|
||||
$debug_print = !empty($_GET['debug']) ? true : false;
|
||||
// how long to wait between each call
|
||||
$debug_wait = 2;
|
||||
// if set to true will print all the debug logs too
|
||||
$mock_debug = !empty($_GET['debug_mock']) ? true : false;
|
||||
// wait in seconds between mock tests
|
||||
$mock_wait = 2;
|
||||
|
||||
if (empty($_GET)) {
|
||||
print "<b>Use _GET parameters to start tests</b>";
|
||||
}
|
||||
|
||||
// open debug file output
|
||||
$fp = fopen('log/debug.' . date('YmdHis') . '.log', 'w');
|
||||
if (!is_resource($fp)) {
|
||||
die("Cannot open log debug file");
|
||||
}
|
||||
|
||||
// run info test (prints ENV vars)
|
||||
$run_info_test = false;
|
||||
// run test to get funds info
|
||||
$run_fund_test = true;
|
||||
// run the normal get/cancel gift card tests
|
||||
$run_gift_tests = true;
|
||||
// run mock error check tests
|
||||
$run_mocks = true;
|
||||
|
||||
// should we print debug info
|
||||
$debug_print = false;
|
||||
// how long to wait between each call
|
||||
$debug_wait = 2;
|
||||
// if set to true will print all the debug logs too
|
||||
$mock_debug = false;
|
||||
// wait in seconds between mock tests
|
||||
$mock_wait = 2;
|
||||
|
||||
if ($run_info_test === true) {
|
||||
$aws = new AmazonIncentives();
|
||||
$aws_check_me = $aws->checkMe();
|
||||
@@ -120,7 +125,7 @@ if ($run_fund_test === true) {
|
||||
}
|
||||
fwrite($fp, writeLog((array)$aws_test));
|
||||
} catch (Exception $e) {
|
||||
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
|
||||
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
|
||||
printException('getAvailableFunds', $e->getCode(), $error, $debug_print);
|
||||
fwrite($fp, writeLog($error));
|
||||
};
|
||||
@@ -152,7 +157,7 @@ if ($run_gift_tests === true) {
|
||||
}
|
||||
fwrite($fp, writeLog((array)$aws_test));
|
||||
} catch (\Exception $e) {
|
||||
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
|
||||
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
|
||||
printException('buyGiftCard', $e->getCode(), $error, $debug_print);
|
||||
fwrite($fp, writeLog($error));
|
||||
}
|
||||
@@ -169,7 +174,7 @@ if ($run_gift_tests === true) {
|
||||
}
|
||||
fwrite($fp, writeLog((array)$aws_test));
|
||||
} catch (\Exception $e) {
|
||||
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
|
||||
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
|
||||
print "AWS: cancelGiftCard: " . $error['status']
|
||||
. " [" . $e->getCode() . "]: "
|
||||
. $error['code'] . " | " . $error['type']
|
||||
@@ -197,7 +202,7 @@ if ($run_gift_tests === true) {
|
||||
}
|
||||
fwrite($fp, writeLog((array)$aws_test));
|
||||
} catch (\Exception $e) {
|
||||
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
|
||||
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
|
||||
printException('buyGiftCard', $e->getCode(), $error, $debug_print);
|
||||
fwrite($fp, writeLog($error));
|
||||
}
|
||||
@@ -221,7 +226,7 @@ if ($run_gift_tests === true) {
|
||||
}
|
||||
fwrite($fp, writeLog((array)$aws_test));
|
||||
} catch (\Exception $e) {
|
||||
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
|
||||
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
|
||||
printException('cancelGiftCard', $e->getCode(), $error, $debug_print);
|
||||
fwrite($fp, writeLog($error));
|
||||
}
|
||||
@@ -242,7 +247,7 @@ if ($run_gift_tests === true) {
|
||||
}
|
||||
fwrite($fp, writeLog((array)$aws_test));
|
||||
} catch (\Exception $e) {
|
||||
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
|
||||
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
|
||||
printException('buyGiftCard', $e->getCode(), $error, $debug_print);
|
||||
fwrite($fp, writeLog($error));
|
||||
}
|
||||
@@ -295,7 +300,7 @@ if ($run_mocks === true) {
|
||||
}
|
||||
fwrite($fp, writeLog((array)$aws_test));
|
||||
} catch (Exception $e) {
|
||||
$error = AmazonIncentives::decodeExceptionMessage($e->getMessage());
|
||||
$error = AmazonErrors::decodeExceptionMessage($e->getMessage());
|
||||
print "AWS: MOCK: " . $creation_id . ": buyGiftCard: " . $error['status']
|
||||
. " [" . $e->getCode() . "]: "
|
||||
. $error['code'] . " | " . $error['type']
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
21
test/phpUnit/Hook/BypassFinalHook.php
Normal file
21
test/phpUnit/Hook/BypassFinalHook.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
// strip the final name from a to be mocked class
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace test\phpUnit\Hook;
|
||||
|
||||
use DG\BypassFinals;
|
||||
use PHPUnit\Runner\BeforeFirstTestHook;
|
||||
|
||||
// only works if it is the FIRST load and not before EACH test
|
||||
final class BypassFinalHook implements BeforeFirstTestHook
|
||||
{
|
||||
public function executeBeforeFirstTest(): void
|
||||
{
|
||||
BypassFinals::enable();
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
1
vendor/composer/autoload_classmap.php
vendored
1
vendor/composer/autoload_classmap.php
vendored
@@ -7,6 +7,7 @@ $baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'DG\\BypassFinals' => $vendorDir . '/dg/bypass-finals/src/BypassFinals.php',
|
||||
'PHPUnit\\Exception' => $vendorDir . '/phpunit/phpunit/src/Exception.php',
|
||||
'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php',
|
||||
'PHPUnit\\Framework\\Assert' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert.php',
|
||||
|
||||
1
vendor/composer/autoload_static.php
vendored
1
vendor/composer/autoload_static.php
vendored
@@ -76,6 +76,7 @@ class ComposerStaticInit0c8f6bec90a6d60040a922f19a1f0e64
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'DG\\BypassFinals' => __DIR__ . '/..' . '/dg/bypass-finals/src/BypassFinals.php',
|
||||
'PHPUnit\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php',
|
||||
'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php',
|
||||
'PHPUnit\\Framework\\Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert.php',
|
||||
|
||||
58
vendor/composer/installed.json
vendored
58
vendor/composer/installed.json
vendored
@@ -1,5 +1,62 @@
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "dg/bypass-finals",
|
||||
"version": "dev-master",
|
||||
"version_normalized": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dg/bypass-finals.git",
|
||||
"reference": "fb62dc6ab1a097e234fa1567943d8e87ea4d0842"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dg/bypass-finals/zipball/fb62dc6ab1a097e234fa1567943d8e87ea4d0842",
|
||||
"reference": "fb62dc6ab1a097e234fa1567943d8e87ea4d0842",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.3",
|
||||
"phpstan/phpstan": "^0.12"
|
||||
},
|
||||
"time": "2022-04-14T12:24:34+00:00",
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause",
|
||||
"GPL-2.0",
|
||||
"GPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
}
|
||||
],
|
||||
"description": "Removes final keyword from source code on-the-fly and allows mocking of final methods and classes",
|
||||
"keywords": [
|
||||
"finals",
|
||||
"mocking",
|
||||
"phpunit",
|
||||
"testing",
|
||||
"unit"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/dg/bypass-finals/issues",
|
||||
"source": "https://github.com/dg/bypass-finals/tree/master"
|
||||
},
|
||||
"install-path": "../dg/bypass-finals"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
"version": "1.5.x-dev",
|
||||
@@ -2184,6 +2241,7 @@
|
||||
],
|
||||
"dev": true,
|
||||
"dev-package-names": [
|
||||
"dg/bypass-finals",
|
||||
"doctrine/instantiator",
|
||||
"gullevek/dotenv",
|
||||
"myclabs/deep-copy",
|
||||
|
||||
15
vendor/composer/installed.php
vendored
15
vendor/composer/installed.php
vendored
@@ -5,11 +5,22 @@
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => '1e836e9a2bd6e41c4555cf917fe645b453cb5b79',
|
||||
'reference' => 'fd5477269b9a133448cecb5854b784033d6c6c62',
|
||||
'name' => 'gullevek/amazon-incentives',
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'dg/bypass-finals' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../dg/bypass-finals',
|
||||
'aliases' => array(
|
||||
0 => '9999999-dev',
|
||||
),
|
||||
'reference' => 'fb62dc6ab1a097e234fa1567943d8e87ea4d0842',
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'doctrine/instantiator' => array(
|
||||
'pretty_version' => '1.5.x-dev',
|
||||
'version' => '1.5.9999999.9999999-dev',
|
||||
@@ -25,7 +36,7 @@
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => '1e836e9a2bd6e41c4555cf917fe645b453cb5b79',
|
||||
'reference' => 'fd5477269b9a133448cecb5854b784033d6c6c62',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'gullevek/dotenv' => array(
|
||||
|
||||
26
vendor/dg/bypass-finals/composer.json
vendored
Normal file
26
vendor/dg/bypass-finals/composer.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "dg/bypass-finals",
|
||||
"description": "Removes final keyword from source code on-the-fly and allows mocking of final methods and classes",
|
||||
"keywords": ["testing", "unit", "phpunit", "mocking", "finals"],
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.3",
|
||||
"phpstan/phpstan": "^0.12"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
},
|
||||
"scripts": {
|
||||
"phpstan": "phpstan analyse",
|
||||
"tester": "tester tests -s"
|
||||
}
|
||||
}
|
||||
49
vendor/dg/bypass-finals/license.md
vendored
Normal file
49
vendor/dg/bypass-finals/license.md
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
Licenses
|
||||
========
|
||||
|
||||
Good news! You may use Nette Tester under the terms of either
|
||||
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
|
||||
|
||||
|
||||
|
||||
New BSD License
|
||||
---------------
|
||||
|
||||
Copyright (c) 2004, 2013 David Grudl (https://davidgrudl.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of "Nette Tester" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall the copyright owner or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused and on
|
||||
any theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
||||
|
||||
|
||||
|
||||
GNU General Public License
|
||||
--------------------------
|
||||
|
||||
GPL licenses are very very long, so instead of including them here we offer
|
||||
you URLs with full text:
|
||||
|
||||
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
|
||||
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
|
||||
64
vendor/dg/bypass-finals/readme.md
vendored
Normal file
64
vendor/dg/bypass-finals/readme.md
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
Bypass Finals
|
||||
=============
|
||||
|
||||
[](https://packagist.org/packages/dg/bypass-finals)
|
||||
[](https://github.com/dg/bypass-finals/actions)
|
||||
[](https://github.com/dg/bypass-finals/releases)
|
||||
[](https://github.com/dg/bypass-finals/blob/master/license.md)
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Removes final keywords from source code on-the-fly and allows mocking of final methods and classes.
|
||||
It can be used together with any test tool such as PHPUnit, Mockery or [Nette Tester](https://tester.nette.org).
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The recommended way to install is through Composer:
|
||||
|
||||
```
|
||||
composer require dg/bypass-finals --dev
|
||||
```
|
||||
|
||||
It requires PHP version 7.1 (or 5.6 in case of release 1.1) and supports PHP up to 8.1.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Simply call this:
|
||||
|
||||
```php
|
||||
DG\BypassFinals::enable();
|
||||
```
|
||||
|
||||
You need to enable it before the classes you want to remove the final are loaded. So call it as soon as possible,
|
||||
preferably right after `vendor/autoload.php` is loaded.
|
||||
|
||||
Note that final internal PHP classes like `Closure` cannot be mocked.
|
||||
|
||||
You can choose to only bypass finals in specific files or directories:
|
||||
|
||||
```php
|
||||
DG\BypassFinals::setWhitelist([
|
||||
'*/Nette/*',
|
||||
]);
|
||||
```
|
||||
|
||||
This gives you finer control and can solve issues with certain frameworks and libraries.
|
||||
|
||||
You can try to increase performance by using the cache (the directory must exist):
|
||||
|
||||
```php
|
||||
DG\BypassFinals::$cacheDir = __DIR__ . '/tmp';
|
||||
```
|
||||
|
||||
Support Project
|
||||
---------------
|
||||
|
||||
Do you like BypassFinals?
|
||||
|
||||
[](https://nette.org/make-donation?to=bypass-finals)
|
||||
312
vendor/dg/bypass-finals/src/BypassFinals.php
vendored
Normal file
312
vendor/dg/bypass-finals/src/BypassFinals.php
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DG;
|
||||
|
||||
|
||||
/**
|
||||
* Removes keyword final from source codes.
|
||||
*/
|
||||
class BypassFinals
|
||||
{
|
||||
private const PROTOCOL = 'file';
|
||||
|
||||
/** @var resource|null */
|
||||
public $context;
|
||||
|
||||
/** @var ?string */
|
||||
public static $cacheDir;
|
||||
|
||||
/** @var resource|null */
|
||||
private $handle;
|
||||
|
||||
/** @var array */
|
||||
private static $pathWhitelist = ['*'];
|
||||
|
||||
/** @var ?object */
|
||||
private static $prevWrapper;
|
||||
|
||||
|
||||
public static function enable(): void
|
||||
{
|
||||
$meta = stream_get_meta_data(fopen(__FILE__, 'r'));
|
||||
self::$prevWrapper = $meta['wrapper_data'] ?? null;
|
||||
stream_wrapper_unregister(self::PROTOCOL);
|
||||
stream_wrapper_register(self::PROTOCOL, self::class);
|
||||
}
|
||||
|
||||
|
||||
public static function setWhitelist(array $whitelist): void
|
||||
{
|
||||
foreach ($whitelist as &$mask) {
|
||||
$mask = strtr($mask, '\\', '/');
|
||||
}
|
||||
|
||||
self::$pathWhitelist = $whitelist;
|
||||
}
|
||||
|
||||
|
||||
public function dir_closedir(): void
|
||||
{
|
||||
closedir($this->handle);
|
||||
}
|
||||
|
||||
|
||||
public function dir_opendir(string $path, int $options): bool
|
||||
{
|
||||
$this->handle = $this->context
|
||||
? $this->native('opendir', $path, $this->context)
|
||||
: $this->native('opendir', $path);
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
|
||||
public function dir_readdir()
|
||||
{
|
||||
return readdir($this->handle);
|
||||
}
|
||||
|
||||
|
||||
public function dir_rewinddir(): bool
|
||||
{
|
||||
return (bool) rewinddir($this->handle);
|
||||
}
|
||||
|
||||
|
||||
public function mkdir(string $path, int $mode, int $options): bool
|
||||
{
|
||||
$recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
|
||||
return $this->context
|
||||
? $this->native('mkdir', $path, $mode, $recursive, $this->context)
|
||||
: $this->native('mkdir', $path, $mode, $recursive);
|
||||
}
|
||||
|
||||
|
||||
public function rename(string $pathFrom, string $pathTo): bool
|
||||
{
|
||||
return $this->context
|
||||
? $this->native('rename', $pathFrom, $pathTo, $this->context)
|
||||
: $this->native('rename', $pathFrom, $pathTo);
|
||||
}
|
||||
|
||||
|
||||
public function rmdir(string $path, int $options): bool
|
||||
{
|
||||
return $this->context
|
||||
? $this->native('rmdir', $path, $this->context)
|
||||
: $this->native('rmdir', $path);
|
||||
}
|
||||
|
||||
|
||||
public function stream_cast(int $castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
|
||||
public function stream_close(): void
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
|
||||
public function stream_eof(): bool
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
|
||||
public function stream_flush(): bool
|
||||
{
|
||||
return fflush($this->handle);
|
||||
}
|
||||
|
||||
|
||||
public function stream_lock(int $operation): bool
|
||||
{
|
||||
return $operation
|
||||
? flock($this->handle, $operation)
|
||||
: true;
|
||||
}
|
||||
|
||||
|
||||
public function stream_metadata(string $path, int $option, $value): bool
|
||||
{
|
||||
switch ($option) {
|
||||
case STREAM_META_TOUCH:
|
||||
return $this->native('touch', $path, $value[0] ?? time(), $value[1] ?? time());
|
||||
case STREAM_META_OWNER_NAME:
|
||||
case STREAM_META_OWNER:
|
||||
return $this->native('chown', $path, $value);
|
||||
case STREAM_META_GROUP_NAME:
|
||||
case STREAM_META_GROUP:
|
||||
return $this->native('chgrp', $path, $value);
|
||||
case STREAM_META_ACCESS:
|
||||
return $this->native('chmod', $path, $value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool
|
||||
{
|
||||
$usePath = (bool) ($options & STREAM_USE_PATH);
|
||||
if ($mode === 'rb' && pathinfo($path, PATHINFO_EXTENSION) === 'php' && self::isPathInWhiteList($path)) {
|
||||
if (self::$prevWrapper) {
|
||||
$content = null;
|
||||
self::$prevWrapper->stream_open($path, $mode, $options, $openedPath);
|
||||
while (!self::$prevWrapper->stream_eof()) {
|
||||
$content .= self::$prevWrapper->stream_read(8192);
|
||||
}
|
||||
|
||||
self::$prevWrapper->stream_close();
|
||||
} else {
|
||||
$content = $this->native('file_get_contents', $path, $usePath, $this->context);
|
||||
}
|
||||
if (!is_string($content)) {
|
||||
return false;
|
||||
}
|
||||
$modified = self::cachedRemoveFinals($content);
|
||||
if ($modified !== $content) {
|
||||
$this->handle = tmpfile();
|
||||
$this->native('fwrite', $this->handle, $modified);
|
||||
$this->native('fseek', $this->handle, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$this->handle = $this->context
|
||||
? $this->native('fopen', $path, $mode, $usePath, $this->context)
|
||||
: $this->native('fopen', $path, $mode, $usePath);
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
|
||||
public function stream_read(int $count)
|
||||
{
|
||||
return fread($this->handle, $count);
|
||||
}
|
||||
|
||||
|
||||
public function stream_seek(int $offset, int $whence = SEEK_SET): bool
|
||||
{
|
||||
return fseek($this->handle, $offset, $whence) === 0;
|
||||
}
|
||||
|
||||
|
||||
public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return fstat($this->handle);
|
||||
}
|
||||
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return ftell($this->handle);
|
||||
}
|
||||
|
||||
|
||||
public function stream_truncate(int $newSize): bool
|
||||
{
|
||||
return ftruncate($this->handle, $newSize);
|
||||
}
|
||||
|
||||
|
||||
public function stream_write(string $data)
|
||||
{
|
||||
return fwrite($this->handle, $data);
|
||||
}
|
||||
|
||||
|
||||
public function unlink(string $path): bool
|
||||
{
|
||||
return $this->native('unlink', $path);
|
||||
}
|
||||
|
||||
|
||||
public function url_stat(string $path, int $flags)
|
||||
{
|
||||
$func = $flags & STREAM_URL_STAT_LINK ? 'lstat' : 'stat';
|
||||
return $flags & STREAM_URL_STAT_QUIET
|
||||
? @$this->native($func, $path)
|
||||
: $this->native($func, $path);
|
||||
}
|
||||
|
||||
|
||||
private function native(string $func)
|
||||
{
|
||||
stream_wrapper_restore(self::PROTOCOL);
|
||||
try {
|
||||
return $func(...array_slice(func_get_args(), 1));
|
||||
} finally {
|
||||
stream_wrapper_unregister(self::PROTOCOL);
|
||||
stream_wrapper_register(self::PROTOCOL, self::class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function cachedRemoveFinals(string $code): string
|
||||
{
|
||||
if (stripos($code, 'final') === false) {
|
||||
// do nothing
|
||||
|
||||
} elseif (self::$cacheDir) {
|
||||
$hash = sha1($code);
|
||||
if ($file = @fopen(self::$cacheDir . '/' . $hash, 'r')) { // @ may not exist
|
||||
flock($file, LOCK_SH);
|
||||
if ($res = stream_get_contents($file)) {
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
$code = self::removeFinals($code);
|
||||
if ($file = @fopen(self::$cacheDir . '/' . $hash, 'x')) { // @ may exist
|
||||
flock($file, LOCK_EX);
|
||||
fwrite($file, $code);
|
||||
}
|
||||
|
||||
} else {
|
||||
$code = self::removeFinals($code);
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
|
||||
public static function removeFinals(string $code): string
|
||||
{
|
||||
try {
|
||||
$tokens = token_get_all($code, TOKEN_PARSE);
|
||||
} catch (\ParseError $e) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
$code = '';
|
||||
foreach ($tokens as $token) {
|
||||
$code .= is_array($token)
|
||||
? ($token[0] === T_FINAL ? '' : $token[1])
|
||||
: $token;
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
|
||||
private static function isPathInWhiteList(string $path): bool
|
||||
{
|
||||
$path = strtr($path, '\\', '/');
|
||||
foreach (self::$pathWhitelist as $mask) {
|
||||
if (fnmatch($mask, $path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user