Skip to content
This repository has been archived by the owner on Jan 20, 2024. It is now read-only.

Commit

Permalink
W-5884747 - In memory cache support (#134)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
sfcbetiuc authored and sfdrogojan committed Mar 8, 2019
1 parent 2f1ac67 commit 8018468
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 51 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -85,7 +89,7 @@ The following code is an example of a minimal composer.json file:
<pre>
{
"require": {
"salesforce-mc/fuel-sdk-php": "1.2.1"
"salesforce-mc/fuel-sdk-php": "1.2.2"
}
}
</pre>
Expand Down
2 changes: 1 addition & 1 deletion config.php.template
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ return array(
'proxyhost' => 'localhost',
'proxyport' => '8080',
'proxyusername' => '',
'proxypassword' => '',
'proxypassword' => ''
);
42 changes: 5 additions & 37 deletions src/ET_CacheService.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
Expand All @@ -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));
}

}
23 changes: 17 additions & 6 deletions src/ET_Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -88,6 +92,7 @@ class ET_Client extends SoapClient
* <i><b>proxyport</b></i> - proxy server prot number</br>
* <i><b>proxyusername</b></i> - proxy server user name</br>
* <i><b>proxypassword</b></i> - proxy server password</br>
* <i><b>sslverifypeer</b></i> - Require verification of peer name</br>
*/
function __construct($getWSDL = false, $debug = false, $params = null)
{
Expand Down Expand Up @@ -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)
{
Expand All @@ -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'];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)) {
Expand Down
21 changes: 15 additions & 6 deletions src/ET_Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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;
}
}
?>
150 changes: 150 additions & 0 deletions tests/CacheServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php
namespace FuelSdk {

/**
* Overwrites the time() function in order to be able to control it from the tests
* Returns the Test\CacheServiceTest::$now value if set or the default time() value, otherwise.
* @return int
*/
function time()
{
return Test\CacheServiceTest::$now ?: \time();
}
}

namespace FuelSdk\Test {

use FuelSdk\ET_CacheService;

/**
* @covers \FuelSdk\ET_CacheService
*/
final class CacheServiceTest extends \PHPUnit_Framework_TestCase
{
public static $now;
private $currentTime;
const CACHE_TIME_IN_SECONDS = 10 * 60;
const CLIENT_ID_1 = 'id1';
const CLIENT_SECRET_1 = 'secret1';
const SOAP_URL_1 = 'http://soap1.asmx';
const CLIENT_ID_2 = 'id2';
const CLIENT_SECRET_2 = 'secret2';
const SOAP_URL_2 = 'http://soap2.asmx';

public function setup()
{
$this->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;
}
}
}

0 comments on commit 8018468

Please sign in to comment.