From 8018468706fd796bd63d18f0af3ea8b1de088988 Mon Sep 17 00:00:00 2001 From: sfcbetiuc <42634794+sfcbetiuc@users.noreply.github.com> Date: Fri, 8 Mar 2019 12:24:24 +0200 Subject: [PATCH] W-5884747 - In memory cache support (#134) * started implementation * ET_CacheService refactor and tests. * Proxy changes * Refactoring * Updated readme file * Updated version in getSDKVersion function and in the Composer section in the readme * Defaulted sslverifypeer to false --- README.md | 6 +- config.php.template | 2 +- src/ET_CacheService.php | 42 ++--------- src/ET_Client.php | 23 ++++-- src/ET_Util.php | 21 ++++-- tests/CacheServiceTest.php | 150 +++++++++++++++++++++++++++++++++++++ 6 files changed, 193 insertions(+), 51 deletions(-) create mode 100644 tests/CacheServiceTest.php diff --git a/README.md b/README.md index 0628df1..0902e5e 100755 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ Salesforce Marketing Cloud Fuel SDK for PHP ## Overview ## The Fuel SDK for PHP provides easy access to Salesforce Marketic Cloud's Fuel API Family services, including a collection of REST and SOAP API. These APIs provide access to Salesforce Marketing Cloud (previously called ExactTarget) functionality via common collection types such as array/hash. +## New Features in Version 1.2.2 ## + +* Added support for in memory cache. + ## New Features in Version 1.2.1 ## * Updated robrichards/wse-php dependency version to 2.0.3 @@ -85,7 +89,7 @@ The following code is an example of a minimal composer.json file:
 {
     "require": {
-        "salesforce-mc/fuel-sdk-php": "1.2.1"
+        "salesforce-mc/fuel-sdk-php": "1.2.2"
     }
 }
 
diff --git a/config.php.template b/config.php.template index 2a50be6..e483f98 100755 --- a/config.php.template +++ b/config.php.template @@ -11,5 +11,5 @@ return array( 'proxyhost' => 'localhost', 'proxyport' => '8080', 'proxyusername' => '', - 'proxypassword' => '', + 'proxypassword' => '' ); diff --git a/src/ET_CacheService.php b/src/ET_CacheService.php index b9392d1..9b89f6d 100644 --- a/src/ET_CacheService.php +++ b/src/ET_CacheService.php @@ -2,13 +2,11 @@ namespace FuelSdk; - class ET_CacheService { - private $_identifier; - private $_filePath = '../.cache'; private $_cacheMinutes = 10; + private static $cachedSoapUrls; public function __construct($clientId, $clientSecret) { @@ -17,10 +15,11 @@ public function __construct($clientId, $clientSecret) public function get() { - $cache = $this->_getOrCreateFile(); - $data = $cache->{$this->_identifier}; $now = time(); + $data = ET_CacheService::$cachedSoapUrls[$this->_identifier]; if (!$data || !$data->expires || $data->expires < $now) { + // remove expired data from the array + unset(ET_CacheService::$cachedSoapUrls[$this->_identifier]); return null; } else { return $data; @@ -30,40 +29,9 @@ public function get() public function write($url) { $expires = time() + $this->_cacheMinutes * 60; - $cache = $this->_getOrCreateFile(); $data = new \stdClass(); $data->expires = $expires; $data->url = $url; - $cache->{$this->_identifier} = $data; - $this->_writeFile($cache); - } - - public function clear() - { - $cache = $this->_getOrCreateFile(); - unset($cache->{$this->_identifier}); - $this->_writeFile($cache); - } - - private function _getOrCreateFile() - { - if (file_exists($this->_filePath)) { - return $this->_readFile(); - } else { - $data = new \stdClass(); - $this->_writeFile($data); - return $data; - } - } - - private function _writeFile($contents) - { - file_put_contents($this->_filePath, json_encode($contents)); + ET_CacheService::$cachedSoapUrls[$this->_identifier] = $data; } - - private function _readFile() - { - return json_decode(file_get_contents($this->_filePath)); - } - } \ No newline at end of file diff --git a/src/ET_Client.php b/src/ET_Client.php index ebcdbfc..30ce5e8 100755 --- a/src/ET_Client.php +++ b/src/ET_Client.php @@ -55,6 +55,10 @@ class ET_Client extends SoapClient * @var string Proxy password. */ public $proxyPassword; + /** + * @var boolean Require verification of peer name. + */ + public $sslVerifyPeer; /** * @var string APIs hostname @@ -88,6 +92,7 @@ class ET_Client extends SoapClient * proxyport - proxy server prot number
* proxyusername - proxy server user name
* proxypassword - proxy server password
+ * sslverifypeer - Require verification of peer name
*/ function __construct($getWSDL = false, $debug = false, $params = null) { @@ -120,6 +125,7 @@ function __construct($getWSDL = false, $debug = false, $params = null) if (array_key_exists('proxyport', $config)){$this->proxyPort = $config['proxyport'];} if (array_key_exists('proxyusername', $config)){$this->proxyUserName = $config['proxyusername'];} if (array_key_exists('proxypassword', $config)){$this->proxyPassword = $config['proxypassword'];} + if (array_key_exists('sslverifypeer', $config)){$this->sslVerifyPeer = $config['sslverifypeer'];} } if ($params) { @@ -133,7 +139,8 @@ function __construct($getWSDL = false, $debug = false, $params = null) if ($params && array_key_exists('proxyhost', $params)){$this->proxyHost = $params['proxyhost'];} if ($params && array_key_exists('proxyport', $params)){$this->proxyPort = $params['proxyport'];} if ($params && array_key_exists('proxyusername', $params)) {$this->proxyUserName = $params['proxyusername'];} - if ($params && array_key_exists('proxypassword', $params)) {$this->proxyPassword = $params['proxypassword'];} + if ($params && array_key_exists('proxypassword', $params)) {$this->proxyPassword = $params['proxypassword'];} + if ($params && array_key_exists('sslverifypeer', $params)) {$this->sslVerifyPeer = $params['sslverifypeer'];} if ($params && array_key_exists('baseUrl', $params)) { $this->baseUrl = $params['baseUrl']; @@ -206,10 +213,14 @@ function __construct($getWSDL = false, $debug = false, $params = null) } } + $context = stream_context_create([ + 'ssl' => [ + 'verify_peer' => ET_Util::shouldVerifySslPeer($this->sslVerifyPeer) + ] + ]); + $soapOptions = array( - 'trace'=>1, - 'exceptions'=>0, - 'connection_timeout'=>120, + 'stream_context' => $context ); if (!empty($this->proxyHost)) { $soapOptions['proxy_host'] = $this->proxyHost; @@ -330,7 +341,7 @@ function CreateWSDL($wsdlLoc) function GetLastModifiedDate($remotepath) { $curl = curl_init($remotepath); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, ET_Util::shouldVerifySslPeer($this->sslVerifyPeer)); curl_setopt($curl, CURLOPT_NOBODY, true); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_FILETIME, true); @@ -386,7 +397,7 @@ function __doRequest($request, $location, $saction, $version, $one_way = 0) curl_setopt ($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt ($ch, CURLOPT_POSTFIELDS, $content); curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ET_Util::shouldVerifySslPeer($this->sslVerifyPeer)); curl_setopt($ch, CURLOPT_USERAGENT, ET_Util::getSDKVersion()); if (!empty($this->proxyHost)) { diff --git a/src/ET_Util.php b/src/ET_Util.php index 7900812..6c18cf1 100644 --- a/src/ET_Util.php +++ b/src/ET_Util.php @@ -32,7 +32,7 @@ public static function restGet($url, $authStub, $isAuthConnection="") curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Disable VerifyPeer for SSL - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, self::shouldVerifySslPeer($authStub->sslVerifyPeer)); //proxy setting if (!empty($authStub->proxyHost)) { @@ -86,7 +86,7 @@ public static function restPost($url, $content, $authStub, $isAuthConnection="") curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Disable VerifyPeer for SSL - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, self::shouldVerifySslPeer($authStub->sslVerifyPeer)); //proxy setting if (!empty($authStub->proxyHost)) { @@ -141,7 +141,7 @@ public static function restPatch($url, $content, $authStub, $isAuthConnection="" curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH" ); // Disable VerifyPeer for SSL - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, self::shouldVerifySslPeer($authStub->sslVerifyPeer)); //proxy setting if (!empty($authStub->proxyHost)) { @@ -195,7 +195,7 @@ public static function restPut($url, $content, $authStub, $isAuthConnection="") curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT" ); // Disable VerifyPeer for SSL - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, self::shouldVerifySslPeer($authStub->sslVerifyPeer)); //proxy setting if (!empty($authStub->proxyHost)) { @@ -242,7 +242,7 @@ public static function restDelete($url, $authStub, $isAuthConnection="") curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Disable VerifyPeer for SSL - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, self::shouldVerifySslPeer($authStub->sslVerifyPeer)); // Set CustomRequest up for Delete curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); @@ -288,8 +288,17 @@ public static function isAssoc($array) */ public static function getSDKVersion() { - return "FuelSDK-PHP-v1.2.1"; + return "FuelSDK-PHP-v1.2.2"; } + /** + * Returns true if the sslverifypeer config value is explicitly set to true, otherwise false. + * @param $configValue The config value for the sslverifypeer config key + * @return bool + */ + public static function shouldVerifySslPeer($configValue) + { + return $configValue === true ? true : false; + } } ?> \ No newline at end of file diff --git a/tests/CacheServiceTest.php b/tests/CacheServiceTest.php new file mode 100644 index 0000000..3f86019 --- /dev/null +++ b/tests/CacheServiceTest.php @@ -0,0 +1,150 @@ +currentTime = time(); + CacheServiceTest::$now = $this->currentTime; + } + + public function testWhenNewInstanceIsCreatedGetReturnsNull() + { + // Arrange + $sut = new ET_CacheService(self::CLIENT_ID_1, self::CLIENT_SECRET_1); + // Act + $cachedValue = $sut->get(); + // Assert + $this->assertNull($cachedValue); + } + + public function testAnUrlIsCachedFor10Minutes() + { + // Arrange + $sut = new ET_CacheService(self::CLIENT_ID_1, self::CLIENT_SECRET_1); + $sut->write(self::SOAP_URL_1); + // Act + $cachedValue = $sut->get(); + // Assert + $this->assertEquals(self::SOAP_URL_1, $cachedValue->url); + $this->assertEquals($this->currentTime + self::CACHE_TIME_IN_SECONDS, $cachedValue->expires); + } + + public function testGettingACachedValueInTheCacheWindowWillReturnTheCachedValueCorrectly() + { + // Arrange + $sut = new ET_CacheService(self::CLIENT_ID_1, self::CLIENT_SECRET_1); + $sut->write(self::SOAP_URL_1); + // Act + // simulate time passes, but we are still in the cache window + CacheServiceTest::$now += 60; + $cachedValue = $sut->get(); + // Assert + $this->assertEquals(self::SOAP_URL_1, $cachedValue->url); + $this->assertEquals($this->currentTime + self::CACHE_TIME_IN_SECONDS, $cachedValue->expires); + } + + public function testANewInstanceWithTheSameClientIdAndSecretWillGetThePreviouslyCachedValue() + { + // Arrange + $sut = new ET_CacheService(self::CLIENT_ID_1, self::CLIENT_SECRET_1); + + // Act + $sut->write(self::SOAP_URL_1); + $previouslyCachedValue = $sut->get(); + // simulate a new instance with the same client id and secret is created after 10 seconds + CacheServiceTest::$now += 10; + $sut2 = new ET_CacheService(self::CLIENT_ID_1, self::CLIENT_SECRET_1); + $newInstanceCachedValue = $sut2->get(); + + // Assert + $this->assertEquals($previouslyCachedValue->url, $newInstanceCachedValue->url); + $this->assertEquals($previouslyCachedValue->expires, $newInstanceCachedValue->expires); + } + + public function testGettingAnExpiredCachedValueReturnsNull() + { + // Arrange + $sut = new ET_CacheService(self::CLIENT_ID_1, self::CLIENT_SECRET_1); + $sut->write(self::SOAP_URL_1); + // Act + // simulate cache expires + CacheServiceTest::$now += self::CACHE_TIME_IN_SECONDS + 1; + $cachedValue = $sut->get(); + // Assert + $this->assertNull($cachedValue); + } + + public function testSettingAnExpiredCachedValueReturnsTheCorrectSetValue() + { + // Arrange + $sut = new ET_CacheService(self::CLIENT_ID_1, self::CLIENT_SECRET_1); + $sut->write(self::SOAP_URL_1); + + // Act + // simulate cache expires + CacheServiceTest::$now += self::CACHE_TIME_IN_SECONDS + 1; + $this->currentTime = CacheServiceTest::$now; + + $sut->write(self::SOAP_URL_1); + $cachedValue = $sut->get(); + + // Assert + $this->assertEquals(self::SOAP_URL_1, $cachedValue->url); + $this->assertEquals($this->currentTime + self::CACHE_TIME_IN_SECONDS, $cachedValue->expires); + } + + public function testMultipleETCacheServiceInstancesWillSetTheirCachedDataCorrectly() + { + // Arrange + $sut1 = new ET_CacheService(self::CLIENT_ID_1, self::CLIENT_SECRET_1); + $sut1->write(self::SOAP_URL_1); + // simulate a new instance is created after 10 seconds + CacheServiceTest::$now += 10; + $sut2 = new ET_CacheService(self::CLIENT_ID_2, self::CLIENT_SECRET_2); + $sut2->write(self::SOAP_URL_2); + // Act + $cachedValue1 = $sut1->get(); + $cachedValue2 = $sut2->get(); + // Assert + $this->assertEquals(self::SOAP_URL_1, $cachedValue1->url); + $this->assertEquals($this->currentTime + self::CACHE_TIME_IN_SECONDS, $cachedValue1->expires); + $this->assertEquals(self::SOAP_URL_2, $cachedValue2->url); + $this->assertEquals(CacheServiceTest::$now + self::CACHE_TIME_IN_SECONDS, $cachedValue2->expires); + } + + public function tearDown() + { + CacheServiceTest::$now = null; + } + } +}