From 45dd710cc16bfea11ce8879b6837bfddbfbec688 Mon Sep 17 00:00:00 2001
From: schildsCC
Date: Tue, 27 Sep 2016 16:52:51 +0100
Subject: [PATCH 1/3] Added the ability to have code based configuration rather
than file based. Logfiles now no longer have to exist, they are created upon
first use.
---
.gitignore | 1 +
CHANGELOG.md | 5 +
CREDITS | 1 +
INSTALL.md | 4 +-
amazon-config.default.php | 2 -
examples/code_config_examples.php | 68 ++++
includes/classes/AmazonCore.php | 259 +++++++-------
includes/classes/AmazonMWSConfig.php | 329 ++++++++++++++++++
.../classes/AmazonMerchantShipmentCreator.php | 4 +-
includes/classes/AmazonOrder.php | 2 +-
includes/classes/AmazonOrderList.php | 14 +-
includes/classes/AmazonProductsCore.php | 19 +-
includes/classes/AmazonRecommendationCore.php | 16 +-
includes/classes/AmazonSubscriptionCore.php | 13 +-
test-cases/helperFunctions.php | 3 +-
.../includes/classes/AmazonConfigTest.php | 70 ++++
.../includes/classes/AmazonCoreTest.php | 32 +-
17 files changed, 666 insertions(+), 176 deletions(-)
create mode 100644 examples/code_config_examples.php
create mode 100644 includes/classes/AmazonMWSConfig.php
create mode 100644 test-cases/includes/classes/AmazonConfigTest.php
diff --git a/.gitignore b/.gitignore
index 0e4193cf..928a5ca5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ test.php
log.txt
/test-cases/log.txt
composer.lock
+/.idea/
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89f32937..12219473 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
+# 1.4.0 - 2016-08-27
+## Added
+ - Added support for passing code-based configuration, rather than file based, using the new AmazonMWSConfig class
+ - Log files no longer need to exist before the library is used, if they can be created they will be upon first use.
+
## 1.3.0 - 2016-08-03
### Added
- Travis support
diff --git a/CREDITS b/CREDITS
index e7831257..2fbfe14a 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1 +1,2 @@
The phpAmazonMWS library was designed and written by Thomas Hernandez (peardian at gmail) for the CPI Group.
+The v1.4.0 refactoring when adding the AmazonMWSConfig class was done by Steve Childs (stevechilds76 at gmail) for Color Confidence (UK).
\ No newline at end of file
diff --git a/INSTALL.md b/INSTALL.md
index 668b6e68..4b1f6afe 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -2,7 +2,9 @@
To install, simply add the library to your project. Composer is the default installation tool for this library.
If you do not use Composer for your project, you can still auto-load classes by including the file **includes/classes.php** in the page or function.
-Before you use any commands, you need to create an **amazon-config.php** file with your account credentials. Start by copying the template provided (*amazon-config.default.php*) and renaming the file.
+Before you use any commands, you need to either create an **amazon-config.php** file or define an array with your account credentials.
+For the file approach start by copying the template provided (*amazon-config.default.php*) and renaming the file, for the array based approach
+see the supplied example (code_config_examples.php)
If you are operating outside of the United States, be sure to change the Amazon Service URL to the one matching your region.
diff --git a/amazon-config.default.php b/amazon-config.default.php
index 08f93dab..892b33c5 100644
--- a/amazon-config.default.php
+++ b/amazon-config.default.php
@@ -36,5 +36,3 @@
//Turn off normal logging
$muteLog = false;
-
-?>
diff --git a/examples/code_config_examples.php b/examples/code_config_examples.php
new file mode 100644
index 00000000..100c0189
--- /dev/null
+++ b/examples/code_config_examples.php
@@ -0,0 +1,68 @@
+
+ array('myStoreName' =>
+ array(
+ 'merchantId' => 'AAAAAAAAAAAA',
+ 'marketplaceId' => 'AAAAAAAAAAAAAA',
+ 'keyId' => 'AAAAAAAAAAAAAAAAAAAA',
+ 'secretKey' => 'BABABABABABABABABABABABABABABABABABABABA',
+ 'serviceUrl' => '',
+ 'MWSAuthToken' => '',
+ )
+ ),
+ 'AMAZON_SERVICE_URL' => 'https://mws-eu.amazonservices.com', // eu store
+ 'logpath' => __DIR__ . './logs/amazon_mws.log',
+ 'logfunction' => '',
+ 'muteLog' => false
+);
+
+/**
+ * This function will retrieve a list of all items with quantity that was adjusted within the past 24 hours.
+ * The entire list of items is returned, with each item contained in an array.
+ * Note that this does not relay whether or not the feed had any errors.
+ * To get this information, the feed's results must be retrieved.
+ */
+function getAmazonFeedStatusA(){
+ global $amazonConfig; // only for example purposes, please don't use globals!
+
+
+ try {
+ $amz=new AmazonFeedList($amazonConfig);
+ $amz->setStore('myStoreName'); // Not strictly needed as there is only 1 store in the array and its automatically activated
+ $amz->setTimeLimits('- 24 hours'); //limit time frame for feeds to any updated since the given time
+ $amz->setFeedStatuses(array("_SUBMITTED_", "_IN_PROGRESS_", "_DONE_")); //exclude cancelled feeds
+ $amz->fetchFeedSubmissions(); //this is what actually sends the request
+ return $amz->getFeedList();
+ } catch (Exception $ex) {
+ echo 'There was a problem with the Amazon library. Error: '.$ex->getMessage();
+ }
+}
+
+/**
+ * As above but with an alternative method of creating the config object.
+ */
+function getAmazonFeedStatusB(){
+ global $amazonConfig; // only for example purposes, please don't use globals!
+
+ $configObject = new \AmazonMWSConfig($amazonConfig);
+
+ try {
+ // using the getConfigFor method creates another instance of AmazonMWSConfig containing just that store's data
+ // If the method in getAmazonFeedStatusA() has more than 1 store setup in the array, they all are available to
+ // the Amazon MWS library and you can switch between them using setStore(). However, should you want to
+ // have clear seperation between the stores forwhatever reason, you can use getConfigFor to ensure that only
+ // one store is available to the library. They're all still available in the configObject for later use,
+ // calling getConfigFor does not affect the store list within the $configObject
+
+ $amz=new AmazonFeedList($configObject->getConfigFor('myStoreName'));
+ $amz->setTimeLimits('- 24 hours'); //limit time frame for feeds to any updated since the given time
+ $amz->setFeedStatuses(array("_SUBMITTED_", "_IN_PROGRESS_", "_DONE_")); //exclude cancelled feeds
+ $amz->fetchFeedSubmissions(); //this is what actually sends the request
+ return $amz->getFeedList();
+ } catch (Exception $ex) {
+ echo 'There was a problem with the Amazon library. Error: '.$ex->getMessage();
+ }
+}?>
diff --git a/includes/classes/AmazonCore.php b/includes/classes/AmazonCore.php
index 3cc301b3..680e9dac 100644
--- a/includes/classes/AmazonCore.php
+++ b/includes/classes/AmazonCore.php
@@ -99,11 +99,11 @@ abstract class AmazonCore{
protected $throttleStop = false;
protected $storeName;
protected $options;
+ /** @var AmazonMWSConfig */
protected $config;
protected $mockMode = false;
protected $mockFiles;
protected $mockIndex = 0;
- protected $logpath;
protected $env;
protected $rawResponses = array();
@@ -126,11 +126,20 @@ abstract class AmazonCore{
* @param string $config [optional] An alternate config file to set. Used for testing.
*/
protected function __construct($s = null, $mock = false, $m = null, $config = null){
- if (is_null($config)){
- $config = __DIR__.'/../../amazon-config.php';
+ /** If the store argument is an array, then assume its the entire config being passed in */
+ if ($s instanceof \AmazonMWSConfig) {
+ // New config object passed in, use it.
+ $this->setConfig($s);
+ }
+ else {
+ if (is_null($config)){
+ $config = __DIR__.'/../../amazon-config.php';
+ }
+
+ $config = new \AmazonMWSConfig($config);
+ $this->setConfig($config->getConfigFor($s));
+ $this->setStore($s);
}
- $this->setConfig($config);
- $this->setStore($s);
$this->setMock($mock,$m);
$this->env=__DIR__.'/../../environment.php';
@@ -353,19 +362,26 @@ protected function checkResponse($r){
* This method can be used to change the config file after the object has
* been initiated. The file will not be set if it cannot be found or read.
* This is useful for testing, in cases where you want to use a different file.
- * @param string $path The path to the config file.
+ * @param string|\AmazonMWSConfig $config The path to the config file.
* @throws Exception If the file cannot be found or read.
*/
- public function setConfig($path){
- if (file_exists($path) && is_readable($path)){
- include($path);
- $this->config = $path;
- $this->setLogPath($logpath);
- if (isset($AMAZON_SERVICE_URL)) {
- $this->urlbase = rtrim($AMAZON_SERVICE_URL, '/') . '/';
+ public function setConfig($config){
+
+ if ($config instanceof \AmazonMWSConfig) {
+ $this->config = $config;
+ }
+ else {
+ if (file_exists($config) && is_readable($config)){
+ $this->config = new \AmazonMWSConfig($config);
+ } else {
+ throw new Exception("Config file does not exist or cannot be read! ($config)");
}
- } else {
- throw new Exception("Config file does not exist or cannot be read! ($path)");
+ }
+ $this->urlbase = $config->getEndPoint();
+
+ // If the current storeName doesn't exist in the stores array any more, reset it to the first in the list of stores
+ if (empty($this->storeName) OR ($this->config->storeExists($this->storeName) === false)) {
+ $this->setStore(); // Passing no argument defaults it to the first store.
}
}
@@ -375,15 +391,11 @@ public function setConfig($path){
* Use this method to change the log file used. This method is called
* each time the config file is changed.
* @param string $path The path to the log file.
- * @throws Exception If the file cannot be found or read.
*/
public function setLogPath($path){
- if (file_exists($path) && is_readable($path)){
- $this->logpath = $path;
- } else {
- throw new Exception("Log file does not exist or cannot be read! ($path)");
- }
-
+ // We move the validation from here into the Config object to save on duplication.
+ // Its also automatically enables logging when set.
+ $this->config->setLogFile($path);
}
/**
@@ -394,49 +406,45 @@ public function setLogPath($path){
* for making requests with Amazon. If the store cannot be found in the
* config file, or if any of the key values are missing,
* the incident will be logged.
- * @param string $s [optional] The store name to look for.
+ * @param string $storeName [optional]
The store name to look for.
* This parameter is not required if there is only one store defined in the config file.
* @throws Exception If the file can't be found.
*/
- public function setStore($s=null){
- if (file_exists($this->config)){
- include($this->config);
- } else {
- throw new Exception("Config file does not exist!");
- }
-
- if (empty($store) || !is_array($store)) {
+ public function setStore($storeName = null){
+ if ($this->config->getStoreCount() === 0) {
throw new Exception("No stores defined!");
}
-
- if (!isset($s) && count($store)===1) {
- $s=key($store);
+
+ // If no store name is passed, default it to the first store.
+ if (empty($storeName)) {
+ $storeName = key($this->config->getStores());
}
- if(array_key_exists($s, $store)){
- $this->storeName = $s;
- if(array_key_exists('merchantId', $store[$s])){
- $this->options['SellerId'] = $store[$s]['merchantId'];
+ if(array_key_exists($storeName, $this->config->getStores())){
+ $this->storeName = $storeName;
+ $store = $this->config->getStore($storeName);
+ if(array_key_exists('merchantId', $store)){
+ $this->options['SellerId'] = $store['merchantId'];
} else {
$this->log("Merchant ID is missing!",'Warning');
}
- if(array_key_exists('keyId', $store[$s])){
- $this->options['AWSAccessKeyId'] = $store[$s]['keyId'];
+ if(array_key_exists('keyId', $store)){
+ $this->options['AWSAccessKeyId'] = $store['keyId'];
} else {
$this->log("Access Key ID is missing!",'Warning');
}
- if(!array_key_exists('secretKey', $store[$s])){
+ if(!array_key_exists('secretKey', $store)){
$this->log("Secret Key is missing!",'Warning');
}
- if (!empty($store[$s]['serviceUrl'])) {
- $this->urlbase = $store[$s]['serviceUrl'];
+ if (!empty($store['serviceUrl'])) {
+ $this->urlbase = $store['serviceUrl'];
}
- if (!empty($store[$s]['MWSAuthToken'])) {
- $this->options['MWSAuthToken'] = $store[$s]['MWSAuthToken'];
+ if (!empty($store['MWSAuthToken'])) {
+ $this->options['MWSAuthToken'] = $store['MWSAuthToken'];
}
} else {
- $this->log("Store $s does not exist!",'Warning');
+ $this->log("Store $storeName does not exist!",'Warning');
}
}
@@ -467,13 +475,8 @@ public function setThrottleStop($b=true) {
protected function log($msg, $level = 'Info'){
if ($msg != false) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
-
- if (file_exists($this->config)){
- include($this->config);
- } else {
- throw new Exception("Config file does not exist!");
- }
- if (isset($logfunction) && $logfunction != '' && function_exists($logfunction)){
+ $logCallback = $this->config->getLogCallback();
+ if (empty($this->config->getLogCallback()) === false){
switch ($level){
case('Info'): $loglevel = LOG_INFO; break;
case('Throttle'): $loglevel = LOG_INFO; break;
@@ -481,11 +484,11 @@ protected function log($msg, $level = 'Info'){
case('Urgent'): $loglevel = LOG_ERR; break;
default: $loglevel = LOG_INFO;
}
- call_user_func($logfunction,$msg,$loglevel);
+ call_user_func($logCallback,$msg,$loglevel);
}
- if (isset($muteLog) && $muteLog == true){
- return;
+ if ($this->config->isLoggingDisabled()){
+ return null;
}
if(isset($userName) && $userName != ''){
@@ -511,17 +514,13 @@ protected function log($msg, $level = 'Info'){
}else{
$ip = 'cli';
}
- if (!file_exists($this->logpath)) {
- //attempt to create the file if it does not exist
- file_put_contents($this->logpath, "This is the Amazon log, for Amazon classes to use.\n");
- }
- if (file_exists($this->logpath) && is_writable($this->logpath)){
- $str = "[$level][" . date("Y/m/d H:i:s") . " $name@$ip $fileName:$line $function] " . $msg;
- $fd = fopen($this->logpath, "a+");
+ $str = "[$level][" . date("Y/m/d H:i:s") . " $name@$ip $fileName:$line $function] " . $msg;
+ $fd = fopen($this->config->getLogFile(), "a+");
+ if ($fd !== false) {
fwrite($fd,$str . "\r\n");
fclose($fd);
} else {
- throw new Exception('Error! Cannot write to log! ('.$this->logpath.')');
+ throw new Exception('Error! Cannot write to log! ('.$this->config->getLogFile().')');
}
} else {
return false;
@@ -577,14 +576,9 @@ protected function genTime($time=false){
* @throws Exception if config file or secret key is missing
*/
protected function genQuery(){
- if (file_exists($this->config)){
- include($this->config);
- } else {
- throw new Exception("Config file does not exist!");
- }
-
- if (array_key_exists($this->storeName, $store) && array_key_exists('secretKey', $store[$this->storeName])){
- $secretKey = $store[$this->storeName]['secretKey'];
+ $store = $this->config->getStore($this->storeName);
+ if (array_key_exists('secretKey', $store)){
+ $secretKey = $store['secretKey'];
} else {
throw new Exception("Secret Key is missing!");
}
@@ -629,7 +623,7 @@ protected function sendRequest($url,$param){
*
* @param int $i [optional] If set, retrieves the specific response instead of the last one.
* If the index for the response is not used, FALSE will be returned.
- * @return array associative array of HTTP response or FALSE if not set yet
+ * @return array|bool associative array of HTTP response or FALSE if not set yet
*/
public function getLastResponse($i=NULL) {
if (!isset($i)) {
@@ -644,7 +638,7 @@ public function getLastResponse($i=NULL) {
/**
* Gives all response code received from Amazon.
- * @return array list of associative arrays of HTTP response or FALSE if not set yet
+ * @return array|bool list of associative arrays of HTTP response or FALSE if not set yet
* @see getLastResponse
*/
public function getRawResponses() {
@@ -666,6 +660,7 @@ public function getLastResponseCode() {
if (!empty($last['code'])) {
return $last['code'];
}
+ return null;
}
/**
@@ -682,6 +677,7 @@ public function getLastErrorResponse() {
}
}
}
+ return null;
}
/**
@@ -699,6 +695,7 @@ public function getLastErrorCode() {
return $xml->Error->Code;
}
}
+ return null;
}
/**
@@ -716,6 +713,7 @@ public function getLastErrorMessage() {
return $xml->Error->Message;
}
}
+ return null;
}
/**
@@ -737,28 +735,35 @@ protected function checkToken($xml){
if ($xml && $xml->NextToken && (string)$xml->HasNext != 'false' && (string)$xml->MoreResultsAvailable != 'false'){
$this->tokenFlag = true;
$this->options['NextToken'] = (string)$xml->NextToken;
+ return true;
} else {
unset($this->options['NextToken']);
$this->tokenFlag = false;
+ return false;
}
}
-
- //Functions from Athena:
- /**
- * Get url or send POST data
- * @param string $url
- * @param array $param['Header']
- * $param['Post']
- * @return array $return['ok'] 1 - success, (0,-1) - fail
- * $return['body'] - response
- * $return['error'] - error, if "ok" is not 1
- * $return['head'] - http header
- */
- function fetchURL ($url, $param) {
+
+ public function getConfig() {
+ return $this->config;
+ }
+
+ /** Functions from Athena: */
+
+ /**
+ * Get url or send POST data
+ * @param string $url
+ * @param array $param['Header']
+ * $param['Post']
+ * @return array $return['ok'] 1 - success, (0,-1) - fail
+ * $return['body'] - response
+ * $return['error'] - error, if "ok" is not 1
+ * $return['head'] - http header
+ */
+ function fetchURL ($url, $param) {
$return = array();
-
+
$ch = curl_init();
-
+
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch,CURLOPT_TIMEOUT, 0);
curl_setopt($ch,CURLOPT_FORBID_REUSE, 1);
@@ -773,60 +778,60 @@ function fetchURL ($url, $param) {
curl_setopt($ch,CURLOPT_POSTFIELDS, $param['Post']);
}
}
-
+
$data = curl_exec($ch);
if ( curl_errno($ch) ) {
- $return['ok'] = -1;
- $return['error'] = curl_error($ch);
- return $return;
+ $return['ok'] = -1;
+ $return['error'] = curl_error($ch);
+ return $return;
}
-
+
if (is_numeric(strpos($data, 'HTTP/1.1 100 Continue'))) {
$data=str_replace('HTTP/1.1 100 Continue', '', $data);
}
$data = preg_split("/\r\n\r\n/",$data, 2, PREG_SPLIT_NO_EMPTY);
if (!empty($data)) {
- $return['head'] = ( isset($data[0]) ? $data[0] : null );
- $return['body'] = ( isset($data[1]) ? $data[1] : null );
+ $return['head'] = ( isset($data[0]) ? $data[0] : null );
+ $return['body'] = ( isset($data[1]) ? $data[1] : null );
} else {
- $return['head'] = null;
- $return['body'] = null;
+ $return['head'] = null;
+ $return['body'] = null;
}
-
+
$matches = array();
$data = preg_match("/HTTP\/[0-9.]+ ([0-9]+) (.+)\r\n/",$return['head'], $matches);
if (!empty($matches)) {
- $return['code'] = $matches[1];
- $return['answer'] = $matches[2];
+ $return['code'] = $matches[1];
+ $return['answer'] = $matches[2];
}
-
+
$data = preg_match("/meta http-equiv=.refresh. +content=.[0-9]*;url=([^'\"]*)/i",$return['body'], $matches);
if (!empty($matches)) {
- $return['location'] = $matches[1];
- $return['code'] = '301';
+ $return['location'] = $matches[1];
+ $return['code'] = '301';
}
-
+
if ( $return['code'] == '200' || $return['code'] == '302' ) {
- $return['ok'] = 1;
+ $return['ok'] = 1;
} else {
- $return['error'] = (($return['answer'] and $return['answer'] != 'OK') ? $return['answer'] : 'Something wrong!');
- $return['ok'] = 0;
+ $return['error'] = (($return['answer'] and $return['answer'] != 'OK') ? $return['answer'] : 'Something wrong!');
+ $return['ok'] = 0;
}
-
+
foreach (preg_split('/\n/', $return['head'], -1, PREG_SPLIT_NO_EMPTY) as $value) {
- $data = preg_split('/:/', $value, 2, PREG_SPLIT_NO_EMPTY);
- if (is_array($data) and isset($data['1'])) {
- $return['headarray'][$data['0']] = trim($data['1']);
- }
+ $data = preg_split('/:/', $value, 2, PREG_SPLIT_NO_EMPTY);
+ if (is_array($data) and isset($data['1'])) {
+ $return['headarray'][$data['0']] = trim($data['1']);
+ }
}
-
+
curl_close($ch);
-
+
return $return;
- }
+ }
// End Functions from Athena
- // Functions from Amazon:
+ /** Functions from Amazon: **/
/**
* Reformats the provided string using rawurlencode while also replacing ~, copied from Amazon
*
@@ -846,11 +851,14 @@ protected function _urlencode($value) {
* @return string
*/
protected function _getParametersAsString(array $parameters) {
- $queryParameters = array();
- foreach ($parameters as $key => $value) {
- $queryParameters[] = $key . '=' . $this->_urlencode($value);
- }
- return implode('&', $queryParameters);
+// $queryParameters = array();
+// foreach ($parameters as $key => $value) {
+// $queryParameters[] = $key . '=' . $this->_urlencode($value);
+// }
+// return implode('&', $queryParameters);
+
+ // We may as well use the input method!
+ return http_build_query($parameters);
}
/**
@@ -865,7 +873,6 @@ protected function _signParameters(array $parameters, $key) {
$stringToSign = null;
if (2 === $this->options['SignatureVersion']) {
$stringToSign = $this->_calculateStringToSignV2($parameters);
-// var_dump($stringToSign);
} else {
throw new Exception("Invalid Signature Version specified");
}
@@ -875,7 +882,7 @@ protected function _signParameters(array $parameters, $key) {
/**
* generates the string to sign, copied from Amazon
* @param array $parameters
- * @return type
+ * @return string
*/
protected function _calculateStringToSignV2(array $parameters) {
$data = 'POST';
@@ -919,6 +926,4 @@ protected function _sign($data, $key, $algorithm)
// -- End Functions from Amazon --
-}
-
-?>
+}
\ No newline at end of file
diff --git a/includes/classes/AmazonMWSConfig.php b/includes/classes/AmazonMWSConfig.php
new file mode 100644
index 00000000..0572eb35
--- /dev/null
+++ b/includes/classes/AmazonMWSConfig.php
@@ -0,0 +1,329 @@
+logFile = '';
+
+ // Yes I know this is a change from the previous release which defaulted to always logging,
+ // but to better handle log files not existing, this is required as validation is performed when logging
+ // is enabled
+ $this->loggingDisabled = true;
+
+ $this->logCallback = null;
+ $this->stores = array();
+ $this->endpoint = self::DEFAULT_ENDPOINT;
+
+ if ($configFN instanceof self) {
+ // Config can also be another instance of this class! If so, simply grab the config from it.
+ $this->_getConfigFrom($configFN,true);
+
+ } elseif (is_array($configFN)) {
+ // Get the config out of the array instead.
+ if (array_key_exists('stores',$configFN) === false OR empty($configFN['stores'])) {
+ throw new Exception("Config array does not contain any store data!");
+ }
+
+ if ((array_key_exists('AMAZON_SERVICE_URL',$configFN)) AND (empty($configFN['AMAZON_SERVICE_URL']) === false)) {
+ $this->setEndPoint($configFN['AMAZON_SERVICE_URL']);
+ }
+
+ // Add the stores to the internal array.
+ foreach ($configFN['stores'] as $storeName => $rec) {
+ $this->addStore($storeName,$rec);
+ }
+
+ if ((array_key_exists('logpath',$configFN)) AND (empty($configFN['logpath']) === false)) {
+ $this->setLogFile($configFN['logpath']);
+ }
+
+ if ((array_key_exists('logfunction',$configFN)) AND (empty($configFN['logfunction']) === false)) {
+ $this->setLogCallback($configFN['logfunction']);
+ }
+
+ if (array_key_exists('muteLog',$configFN)) {
+ $this->setLoggingDisabled($configFN['muteLog']);
+ }
+ } else {
+ // Presume its a string (filename)
+ if (file_exists($configFN) AND is_readable($configFN)){
+ include $configFN;
+ } else {
+ throw new Exception("Config file does not exist or cannot be read! ($configFN)");
+ }
+
+ if (empty($store)) {
+ throw new Exception("Config file does not contain any store data! ($configFN)");
+ }
+
+ if (empty($AMAZON_SERVICE_URL) === false) {
+ $this->setEndPoint($AMAZON_SERVICE_URL);
+ }
+
+ // Add the stores to the internal array.
+ foreach ($store as $storeName => $rec) {
+ $this->addStore($storeName,$rec);
+ }
+
+ if (isset($logpath)) {
+ $this->setLogFile($logpath);
+ }
+
+ if (isset($logfunction)) {
+ $this->setLogCallback($logfunction);
+ }
+
+ if (isset($muteLog)) {
+ $this->setLoggingDisabled($muteLog);
+ }
+ }
+ } else {
+ // No Config is also valid if you want to set it up manually.
+ }
+ }
+
+ /**
+ * Copies the config out of the passed source object, optionally copying the store data over.
+ *
+ * @param $source
+ * @param bool $copyStoreData
+ */
+ protected function _getConfigFrom($source,$copyStoreData = false) {
+ // Copy the config over and yes I purposefully didn't call the setters, all the property values should
+ // be valid as they're validated when set.
+ $this->endpoint = $source->endpoint;
+ $this->logCallback = $source->logCallback;
+ $this->logFile = $source->logFile;
+ $this->loggingDisabled = $source->loggingDisabled;
+
+ if ($copyStoreData) {
+ $this->stores = array_merge($source->stores,array()); // Ensure we get a duplicate of the array, not a reference.
+ }
+ }
+
+ /// Getter / Setters
+
+ /**
+ * @return mixed
+ */
+ public function getStores()
+ {
+ return $this->stores;
+ }
+
+ /**
+ * Does this store exists within the configuration?
+ *
+ * @param $storeName
+ * @return bool
+ */
+ public function storeExists($storeName) {
+ return array_key_exists($storeName,$this->stores);
+ }
+
+ /**
+ * Adds a new store to the Array
+ *
+ * @param $storename
+ * @param array $storeData
+ */
+ public function addStore($storename,array $storeData)
+ {
+ $this->stores[$storename] = $storeData;
+ }
+
+ /**
+ * Returns a config class to pass into the constructor of a library call. This enables you to have a master configuration
+ * object and then only pass the config for a particular store into the Amazon calls.
+ *
+ * @param $aStoreName
+ * @return AmazonMWSConfig
+ * @throws Exception
+ */
+ public function getConfigFor($aStoreName) {
+ /** @var AmazonMWSConfig $result */
+
+ if ($this->storeExists($aStoreName) === false) {
+ throw new Exception("The requested store does not exist!");
+ }
+
+ $result = new self;
+ $result->_getConfigFrom($this);
+ $result->addStore($aStoreName,$this->stores[$aStoreName]);
+
+ return $result;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLogFile()
+ {
+ return $this->logFile;
+ }
+
+ /**
+ * @param string $logFile
+ */
+ public function setLogFile($logFile)
+ {
+ $this->logFile = $logFile;
+ // If we set a log file, assume we want to enable logging!
+ $this->setLoggingDisabled(false);
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isLoggingDisabled()
+ {
+ return $this->loggingDisabled;
+ }
+
+ /**
+ * Ok, the point at which the logging is enabled, we do some checks. A Blank filename is permitted if a callback
+ * is configured, but must be configured prior to logging being enabled.
+ *
+ * @param boolean $loggingDisabled
+ */
+ public function setLoggingDisabled($loggingDisabled)
+ {
+ // Cast it to a bool
+ $bValue = (bool) $loggingDisabled;
+
+ if ($bValue === false) {
+ // Ok, we don't want to disable logging, inwhich case we need to ensure we can write to the file.
+ if ((empty($this->logFile)) AND (empty($this->logCallback))) {
+ $this->loggingDisabled = true;
+ return;
+ }
+
+ if (empty($this->logCallback) === false) {
+ // The callback is validated upon setting, so if its not empty, it must be valid.
+ $this->loggingDisabled = false;
+ return;
+ }
+
+ if (is_dir($this->logFile)) {
+ // doh!
+ $this->loggingDisabled = true;
+ return;
+ }
+
+ /** If we get here, then we have configured a log file... **/
+ if ((empty($this->logFile) === false) AND (file_exists($this->logFile) === false)) {
+ // Ok, file doesn't exist, try and create it.
+ $hFile = @fopen($this->logFile,'a+b');
+ if ($hFile !== false) {
+ // File created ok, close it.
+ fclose($hFile);
+ }
+ }
+
+ if ((file_exists($this->logFile) === false) OR (is_writable($this->logFile) === false)){
+ // Ok, either we have no logfile or we do, but it can't be written to.
+ // We won't die, but we'll simply disable logging.
+ $this->loggingDisabled = true;
+ } else {
+ $this->loggingDisabled = false;
+ }
+ }
+ else {
+ // We don't care if logging is disabled, no checks to do!
+ $this->loggingDisabled = true;
+ }
+ }
+
+ /**
+ * Sets the endpoint and ensures we have a trailing path separator.
+ * @param string $endPoint
+ */
+ public function setEndPoint($endPoint) {
+ $this->endpoint = rtrim($endPoint, '/') . '/';
+ }
+
+ public function getEndPoint() {
+ return $this->endpoint;
+ }
+
+ /**
+ * Returns the requested store configuration, or a blank array if the key is invalid.
+ *
+ * @param $aStoreName
+ * @return array|mixed
+ */
+ public function getStore($aStoreName) {
+ if (array_key_exists($aStoreName,$this->stores)) {
+ return $this->stores[$aStoreName];
+ }
+
+ // Invalid key, simply return an empty array
+ return [];
+ }
+
+ /**
+ * Returns the length of the stores array
+ *
+ * @return int
+ */
+ public function getStoreCount() {
+ return count($this->stores);
+ }
+
+ /**
+ * @return callable
+ */
+ public function getLogCallback()
+ {
+ return $this->logCallback;
+ }
+
+ /**
+ * @param callable $logCallback
+ */
+ public function setLogCallback($logCallback)
+ {
+ $this->logCallback = null;
+ if (empty($logCallback) === false AND is_callable($logCallback)) {
+ $this->logCallback = $logCallback;
+ }
+ }
+
+ /**
+ * Gets the marketplace for the store from the configuration data
+ *
+ * @param $storeName
+ * @return mixed|string
+ */
+ public function getStoreMarketPlace($storeName) {
+ $store = $this->getStore($storeName);
+ if(array_key_exists('marketplaceId', $store)){
+ return $store['marketplaceId'];
+ }
+ return '';
+ }
+
+}
\ No newline at end of file
diff --git a/includes/classes/AmazonMerchantShipmentCreator.php b/includes/classes/AmazonMerchantShipmentCreator.php
index 8a1f7f51..f27ee95f 100644
--- a/includes/classes/AmazonMerchantShipmentCreator.php
+++ b/includes/classes/AmazonMerchantShipmentCreator.php
@@ -462,7 +462,7 @@ public function setServiceOffer($id) {
public function fetchServices() {
$services = new AmazonMerchantServiceList($this->storeName, $this->mockMode, $this->mockFiles, $this->config);
$services->mockIndex = $this->mockIndex;
- $services->setLogPath($this->logpath);
+ $services->setLogPath($this->config->getLogFile());
$services->setDetailsByCreator($this);
$services->fetchServices();
return $services;
@@ -547,7 +547,7 @@ protected function parseXML($xml){
}
$this->shipment = new AmazonMerchantShipment($this->storeName, NULL, $xml, $this->mockMode, $this->mockFiles, $this->config);
- $this->shipment->setLogPath($this->logpath);
+ $this->shipment->setLogPath($this->config->getLogFile());
$this->shipment->mockIndex = $this->mockIndex;
}
diff --git a/includes/classes/AmazonOrder.php b/includes/classes/AmazonOrder.php
index 266c841c..a84a41e9 100644
--- a/includes/classes/AmazonOrder.php
+++ b/includes/classes/AmazonOrder.php
@@ -132,7 +132,7 @@ public function fetchItems($token = false){
$token = false;
}
$items = new AmazonOrderItemList($this->storeName,$this->data['AmazonOrderId'],$this->mockMode,$this->mockFiles,$this->config);
- $items->setLogPath($this->logpath);
+ $items->setLogPath($this->config->getLogFile());
$items->mockIndex = $this->mockIndex;
$items->setUseToken($token);
$items->fetchItems();
diff --git a/includes/classes/AmazonOrderList.php b/includes/classes/AmazonOrderList.php
index 20e32185..7f22c61f 100644
--- a/includes/classes/AmazonOrderList.php
+++ b/includes/classes/AmazonOrderList.php
@@ -219,15 +219,11 @@ public function resetMarketplaceFilter(){
}
//reset to store's default marketplace
- if (file_exists($this->config)){
- include($this->config);
- } else {
- throw new Exception('Config file does not exist!');
- }
- if(isset($store[$this->storeName]) && array_key_exists('marketplaceId', $store[$this->storeName])){
- $this->options['MarketplaceId.Id.1'] = $store[$this->storeName]['marketplaceId'];
- } else {
+ $storeMP = $this->config->getStoreMarketPlace($this->storeName);
+ if(empty($storeMP)){
$this->log("Marketplace ID is missing",'Urgent');
+ } else {
+ $this->options['MarketplaceId.Id.1'] = $storeMP;
}
}
@@ -495,7 +491,7 @@ protected function parseXML($xml){
break;
}
$this->orderList[$this->index] = new AmazonOrder($this->storeName,null,$data,$this->mockMode,$this->mockFiles,$this->config);
- $this->orderList[$this->index]->setLogPath($this->logpath);
+ $this->orderList[$this->index]->setLogPath($this->config->getLogFile());
$this->orderList[$this->index]->mockIndex = $this->mockIndex;
$this->index++;
}
diff --git a/includes/classes/AmazonProductsCore.php b/includes/classes/AmazonProductsCore.php
index 08ad18dc..1cd97077 100644
--- a/includes/classes/AmazonProductsCore.php
+++ b/includes/classes/AmazonProductsCore.php
@@ -43,24 +43,21 @@ abstract class AmazonProductsCore extends AmazonCore{
public function __construct($s = null, $mock = false, $m = null, $config = null){
parent::__construct($s, $mock, $m, $config);
include($this->env);
- if (file_exists($this->config)){
- include($this->config);
- } else {
- throw new Exception('Config file does not exist!');
- }
if(isset($AMAZON_VERSION_PRODUCTS)){
$this->urlbranch = 'Products/'.$AMAZON_VERSION_PRODUCTS;
$this->options['Version'] = $AMAZON_VERSION_PRODUCTS;
}
-
- //set the store's marketplace as the default
- if(isset($store[$this->storeName]) && array_key_exists('marketplaceId', $store[$this->storeName])){
- $this->setMarketplace($store[$this->storeName]['marketplaceId']);
- } else {
+
+
+ //reset to store's default marketplace
+ $storeMP = $this->config->getStoreMarketPlace($this->storeName);
+ if(empty($storeMP)){
$this->log("Marketplace ID is missing",'Urgent');
+ } else {
+ $this->setMarketplace($storeMP);
}
-
+
if(isset($THROTTLE_LIMIT_PRODUCT)) {
$this->throttleLimit = $THROTTLE_LIMIT_PRODUCT;
}
diff --git a/includes/classes/AmazonRecommendationCore.php b/includes/classes/AmazonRecommendationCore.php
index 7d8786e4..c225a6dd 100644
--- a/includes/classes/AmazonRecommendationCore.php
+++ b/includes/classes/AmazonRecommendationCore.php
@@ -38,15 +38,11 @@ abstract class AmazonRecommendationCore extends AmazonCore{
* This defaults to FALSE.
* @param array|string $m [optional] The files (or file) to use in Mock Mode.
* @param string $config [optional] An alternate config file to set. Used for testing.
+ * @throws Exception
*/
public function __construct($s = null, $mock = false, $m = null, $config = null){
parent::__construct($s, $mock, $m, $config);
include($this->env);
- if (file_exists($this->config)){
- include($this->config);
- } else {
- throw new Exception('Config file does not exist!');
- }
if (isset($AMAZON_VERSION_RECOMMEND)){
$this->urlbranch = 'Recommendations/' . $AMAZON_VERSION_RECOMMEND;
@@ -59,12 +55,14 @@ public function __construct($s = null, $mock = false, $m = null, $config = null)
if(isset($THROTTLE_TIME_RECOMMEND)) {
$this->throttleTime = $THROTTLE_TIME_RECOMMEND;
}
-
- if (isset($store[$this->storeName]['marketplaceId'])){
- $this->setMarketplace($store[$this->storeName]['marketplaceId']);
+ //reset to store's default marketplace
+ $storeMP = $this->config->getStoreMarketPlace($this->storeName);
+ if(empty($storeMP)){
+ $this->log("Marketplace ID is missing",'Urgent');
} else {
- $this->log("Marketplace ID is missing", 'Urgent');
+ $this->setMarketplace($storeMP);
}
+
}
/**
diff --git a/includes/classes/AmazonSubscriptionCore.php b/includes/classes/AmazonSubscriptionCore.php
index f555b874..7e0004d5 100644
--- a/includes/classes/AmazonSubscriptionCore.php
+++ b/includes/classes/AmazonSubscriptionCore.php
@@ -42,11 +42,6 @@ abstract class AmazonSubscriptionCore extends AmazonCore{
public function __construct($s = null, $mock = false, $m = null, $config = null){
parent::__construct($s, $mock, $m, $config);
include($this->env);
- if (file_exists($this->config)){
- include($this->config);
- } else {
- throw new Exception('Config file does not exist!');
- }
if (isset($AMAZON_VERSION_SUBSCRIBE)){
$this->urlbranch = 'Subscriptions/' . $AMAZON_VERSION_SUBSCRIBE;
@@ -60,10 +55,12 @@ public function __construct($s = null, $mock = false, $m = null, $config = null)
$this->throttleTime = $THROTTLE_TIME_SUBSCRIBE;
}
- if (isset($store[$this->storeName]['marketplaceId'])){
- $this->setMarketplace($store[$this->storeName]['marketplaceId']);
+ //reset to store's default marketplace
+ $storeMP = $this->config->getStoreMarketPlace($this->storeName);
+ if(empty($storeMP)){
+ $this->log("Marketplace ID is missing",'Urgent');
} else {
- $this->log("Marketplace ID is missing", 'Urgent');
+ $this->setMarketplace($storeMP);
}
}
diff --git a/test-cases/helperFunctions.php b/test-cases/helperFunctions.php
index e21a6a36..682f8553 100644
--- a/test-cases/helperFunctions.php
+++ b/test-cases/helperFunctions.php
@@ -38,5 +38,4 @@ function parseLog($s = null){
}
}
return $return;
-}
-?>
+}
\ No newline at end of file
diff --git a/test-cases/includes/classes/AmazonConfigTest.php b/test-cases/includes/classes/AmazonConfigTest.php
new file mode 100644
index 00000000..ce98e673
--- /dev/null
+++ b/test-cases/includes/classes/AmazonConfigTest.php
@@ -0,0 +1,70 @@
+object = new AmazonMWSConfig( __DIR__.'/../../test-config.php');
+ }
+
+ /**
+ * Tears down the fixture, for example, closes a network connection.
+ * This method is called after a test is executed.
+ */
+ protected function tearDown() {
+
+ }
+
+ /**
+ * @covers AmazonMWSConfig::__construct()
+ */
+ public function testParsing() {
+ $this->assertEquals($this->object->getStoreCount(),2);
+ $store = $this->object->getStoreCount('testStore');
+ $this->assertTrue(count($store) != 0); // Check we have some records.
+
+ $this->assertEquals($this->object->getEndPoint(),'https://mws.amazonservices.com/');
+ $this->assertEquals($this->object->getLogCallback(),null);
+ $this->assertEquals($this->object->isLoggingDisabled(),false);
+
+ $store = $this->object->getStoreCount('bad');
+ $this->assertTrue(count($store) == 1); // Check we have some 1 record
+ }
+
+ public function testCreateCopy() {
+ $store = $this->object->getConfigFor('testStore');
+
+
+ $this->assertEquals($this->object->getLogFile(),$store->getLogFile());
+ $this->assertEquals($this->object->getLogCallback(),$store->getLogCallback());
+ $this->assertEquals($this->object->getEndPoint(),$store->getEndPoint());
+ $this->assertEquals($this->object->getStoreCount(),2);
+ $this->assertEquals($this->object->getStore('testStore'),$store->getStore('testStore'));
+
+ }
+
+ public function testCreateCopyAll() {
+ // Passing the already created object in should result in the config being loaded from it.
+ $newConfig = new AmazonMWSConfig($this->object);
+
+ $this->assertEquals($this->object->getStoreCount(),$newConfig->getStoreCount());
+ $this->assertEquals($this->object->getLogFile(),$newConfig->getLogFile());
+ $this->assertEquals($this->object->getLogCallback(),$newConfig->getLogCallback());
+ $stores = array_keys($this->object->getStores());
+ foreach($stores as $storeName) {
+ $this->assertEquals($this->object->getStore($storeName),$newConfig->getStore($storeName));
+ }
+ }
+
+}
+
+require_once('helperFunctions.php');
diff --git a/test-cases/includes/classes/AmazonCoreTest.php b/test-cases/includes/classes/AmazonCoreTest.php
index 56c46052..b1094e39 100644
--- a/test-cases/includes/classes/AmazonCoreTest.php
+++ b/test-cases/includes/classes/AmazonCoreTest.php
@@ -9,6 +9,8 @@ class AmazonCoreTest extends PHPUnit_Framework_TestCase {
* @var AmazonServiceStatus
*/
protected $object;
+ /** @var AmazonMWSConfig */
+ protected $config;
/**
* Sets up the fixture, for example, opens a network connection.
@@ -16,6 +18,7 @@ class AmazonCoreTest extends PHPUnit_Framework_TestCase {
*/
protected function setUp() {
resetLog();
+ $this->config = new AmazonMWSConfig( __DIR__.'/../../test-config.php');
$this->object = new AmazonServiceStatus('testStore', 'Inbound', true, null, __DIR__.'/../../test-config.php');
}
@@ -69,16 +72,32 @@ public function testSetConfig() {
/**
* @covers AmazonCore::setLogPath
- * @expectedException Exception
- * @expectedExceptionMessage Log file does not exist or cannot be read! (no)
*/
public function testSetLogPath() {
- $this->object->setLogPath('no');
+ if (PHP_OS == 'Windows') {
+ $this->object->setLogPath('C:\\BadFolder\\ReallyBadFolder\\BadLogFile.txt');
+ } else {
+ $this->object->setLogPath('/dev/no/file/exists');
+ }
+ $this->assertTrue($this->object->getConfig()->isLoggingDisabled());
+ }
+
+ /**
+ * @covers AmazonCore::setLogPath
+ */
+ public function testSetLogPathToFolder() {
+ if (PHP_OS == 'Windows') {
+ $this->object->setLogPath('C:\\Windows\\'); // !!
+ } else {
+ $this->object->setLogPath('/var/log/');
+ }
+ $this->assertTrue($this->object->getConfig()->isLoggingDisabled());
}
/**
+ * Updated to use the new config class instead.
+ *
* @covers AmazonCore::setStore
- * @todo Implement testSetStore().
*/
public function testSetStore() {
$this->object->setStore('no');
@@ -88,6 +107,11 @@ public function testSetStore() {
resetLog();
$this->object->setStore('bad');
$bad = parseLog();
+ $this->assertEquals('Store bad does not exist!',$bad[0]);
+ resetLog();
+ // Ok, now load the 'bad' store from the config.
+ $this->object->setConfig($this->config->getConfigFor('bad'));
+ $bad = parseLog();
$this->assertEquals('Merchant ID is missing!',$bad[0]);
$this->assertEquals('Access Key ID is missing!',$bad[1]);
$this->assertEquals('Secret Key is missing!',$bad[2]);
From f0f9e6d679ff774c61a320e36e19c1f2d6a5cfba Mon Sep 17 00:00:00 2001
From: schildsCC
Date: Wed, 28 Sep 2016 09:15:11 +0100
Subject: [PATCH 2/3] Fixed error in the log function, reverted changes to
Credits and Change log
---
CHANGELOG.md | 5 -----
CREDITS | 1 -
includes/classes/AmazonCore.php | 2 +-
includes/classes/AmazonMWSConfig.php | 26 +++++++++++++++++++++++++-
4 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 12219473..89f32937 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,11 +2,6 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
-# 1.4.0 - 2016-08-27
-## Added
- - Added support for passing code-based configuration, rather than file based, using the new AmazonMWSConfig class
- - Log files no longer need to exist before the library is used, if they can be created they will be upon first use.
-
## 1.3.0 - 2016-08-03
### Added
- Travis support
diff --git a/CREDITS b/CREDITS
index 2fbfe14a..e7831257 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1,2 +1 @@
The phpAmazonMWS library was designed and written by Thomas Hernandez (peardian at gmail) for the CPI Group.
-The v1.4.0 refactoring when adding the AmazonMWSConfig class was done by Steve Childs (stevechilds76 at gmail) for Color Confidence (UK).
\ No newline at end of file
diff --git a/includes/classes/AmazonCore.php b/includes/classes/AmazonCore.php
index 680e9dac..babcf9c5 100644
--- a/includes/classes/AmazonCore.php
+++ b/includes/classes/AmazonCore.php
@@ -476,7 +476,7 @@ protected function log($msg, $level = 'Info'){
if ($msg != false) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$logCallback = $this->config->getLogCallback();
- if (empty($this->config->getLogCallback()) === false){
+ if (empty($logCallback) === false){
switch ($level){
case('Info'): $loglevel = LOG_INFO; break;
case('Throttle'): $loglevel = LOG_INFO; break;
diff --git a/includes/classes/AmazonMWSConfig.php b/includes/classes/AmazonMWSConfig.php
index 0572eb35..37599929 100644
--- a/includes/classes/AmazonMWSConfig.php
+++ b/includes/classes/AmazonMWSConfig.php
@@ -1,5 +1,29 @@
Date: Thu, 29 Sep 2016 13:54:55 +0100
Subject: [PATCH 3/3] Fixed minor error in Core that would occur if logging was
enabled, but there was no logfile specified
---
includes/classes/AmazonCore.php | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/includes/classes/AmazonCore.php b/includes/classes/AmazonCore.php
index babcf9c5..705d1035 100644
--- a/includes/classes/AmazonCore.php
+++ b/includes/classes/AmazonCore.php
@@ -486,8 +486,9 @@ protected function log($msg, $level = 'Info'){
}
call_user_func($logCallback,$msg,$loglevel);
}
-
- if ($this->config->isLoggingDisabled()){
+
+ $fnPath = $this->config->getLogFile();
+ if (($this->config->isLoggingDisabled()) OR (empty($fnPath))){
return null;
}