Ejegg has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/337419 )

Change subject: Cache bank lookups via PSR-6
......................................................................


Cache bank lookups via PSR-6

Includes a simple cache implementation. Adds a new config key 'cache'
at the root of the default view, which should resolve to an object
that implements Psr\Cache\CacheItemPoolInterface.

With this, any project that depends on SmashPig can make its cache
available to SmashPig classes, as long as there is a PSR6 compatible
interface.

Bug: T128692
Change-Id: I457fb093c3777406efbfd4821acac47520af60d2
---
A Core/Cache/HashCache.php
A Core/Cache/HashCacheItem.php
M PaymentProviders/Ingenico/BankPaymentProvider.php
M PaymentProviders/Ingenico/Tests/phpunit/BankPaymentProviderTest.php
M SmashPig.yaml
M composer.json
M composer.lock
7 files changed, 388 insertions(+), 16 deletions(-)

Approvals:
  Cdentinger: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/Core/Cache/HashCache.php b/Core/Cache/HashCache.php
new file mode 100644
index 0000000..19edf98
--- /dev/null
+++ b/Core/Cache/HashCache.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace SmashPig\Core\Cache;
+
+use Psr\Cache\CacheItemInterface;
+use Psr\Cache\CacheItemPoolInterface;
+use Psr\Cache\InvalidArgumentException;
+
+class HashCache implements CacheItemPoolInterface {
+
+       protected $items = array();
+
+       protected $deferredQueue = array();
+
+       /**
+        * Returns a Cache Item representing the specified key.
+        *
+        * This method must always return a CacheItemInterface object, even in 
case of
+        * a cache miss. It MUST NOT return null.
+        *
+        * @param string $key
+        *   The key for which to return the corresponding Cache Item.
+        *
+        * @throws InvalidArgumentException
+        *   If the $key string is not a legal value a 
\Psr\Cache\InvalidArgumentException
+        *   MUST be thrown.
+        *
+        * @return CacheItemInterface
+        *   The corresponding Cache Item.
+        */
+       public function getItem( $key ) {
+               if ( isset( $this->items[$key] ) ) {
+                       return new HashCacheItem( $key, $this->items[$key], 
true );
+               }
+               return new HashCacheItem( $key, null, false );
+       }
+
+       /**
+        * Returns a traversable set of cache items.
+        *
+        * @param string[] $keys
+        *   An indexed array of keys of items to retrieve.
+        *
+        * @throws InvalidArgumentException
+        *   If any of the keys in $keys are not a legal value a 
\Psr\Cache\InvalidArgumentException
+        *   MUST be thrown.
+        *
+        * @return array|\Traversable
+        *   A traversable collection of Cache Items keyed by the cache keys of
+        *   each item. A Cache item will be returned for each key, even if that
+        *   key is not found. However, if no keys are specified then an empty
+        *   traversable MUST be returned instead.
+        */
+       public function getItems( array $keys = array() ) {
+               return array_map( array( $this, 'getItem' ), $keys );
+       }
+
+       /**
+        * Confirms if the cache contains specified cache item.
+        *
+        * Note: This method MAY avoid retrieving the cached value for 
performance reasons.
+        * This could result in a race condition with 
CacheItemInterface::get(). To avoid
+        * such situation use CacheItemInterface::isHit() instead.
+        *
+        * @param string $key
+        *   The key for which to check existence.
+        *
+        * @throws InvalidArgumentException
+        *   If the $key string is not a legal value a 
\Psr\Cache\InvalidArgumentException
+        *   MUST be thrown.
+        *
+        * @return bool
+        *   True if item exists in the cache, false otherwise.
+        */
+       public function hasItem( $key ) {
+               return isset( $this->items[$key] );
+       }
+
+       /**
+        * Deletes all items in the pool.
+        *
+        * @return bool
+        *   True if the pool was successfully cleared. False if there was an 
error.
+        */
+       public function clear() {
+               $this->items = array();
+               return true;
+       }
+
+       /**
+        * Removes the item from the pool.
+        *
+        * @param string $key
+        *   The key to delete.
+        *
+        * @throws InvalidArgumentException
+        *   If the $key string is not a legal value a 
\Psr\Cache\InvalidArgumentException
+        *   MUST be thrown.
+        *
+        * @return bool
+        *   True if the item was successfully removed. False if there was an 
error.
+        */
+       public function deleteItem( $key ) {
+               unset( $this->items[$key] );
+               return true;
+       }
+
+       /**
+        * Removes multiple items from the pool.
+        *
+        * @param string[] $keys
+        *   An array of keys that should be removed from the pool.
+        * @throws InvalidArgumentException
+        *   If any of the keys in $keys are not a legal value a 
\Psr\Cache\InvalidArgumentException
+        *   MUST be thrown.
+        *
+        * @return bool
+        *   True if the items were successfully removed. False if there was an 
error.
+        */
+       public function deleteItems( array $keys ) {
+               array_walk( $keys, array( $this, 'deleteItem' ) );
+               return true;
+       }
+
+       /**
+        * Persists a cache item immediately.
+        *
+        * @param CacheItemInterface $item
+        *   The cache item to save.
+        *
+        * @return bool
+        *   True if the item was successfully persisted. False if there was an 
error.
+        */
+       public function save( CacheItemInterface $item ) {
+               $this->items[$item->getKey()] = $item->get();
+               return true;
+       }
+
+       /**
+        * Sets a cache item to be persisted later.
+        *
+        * @param CacheItemInterface $item
+        *   The cache item to save.
+        *
+        * @return bool
+        *   False if the item could not be queued or if a commit was attempted 
and failed. True otherwise.
+        */
+       public function saveDeferred( CacheItemInterface $item ) {
+               $this->deferredQueue[] = $item;
+               return true;
+       }
+
+       /**
+        * Persists any deferred cache items.
+        *
+        * @return bool
+        *   True if all not-yet-saved items were successfully saved or there 
were none. False otherwise.
+        */
+       public function commit() {
+               $success = true;
+               while ( $deferredItem = array_shift( $this->deferredQueue ) ) {
+                       $success = $success && $this->save( $deferredItem );
+               }
+               return $success;
+       }
+}
diff --git a/Core/Cache/HashCacheItem.php b/Core/Cache/HashCacheItem.php
new file mode 100644
index 0000000..280152f
--- /dev/null
+++ b/Core/Cache/HashCacheItem.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace SmashPig\Core\Cache;
+
+use Psr\Cache\CacheItemInterface;
+
+class HashCacheItem implements CacheItemInterface {
+
+       protected $key;
+       protected $value;
+       protected $hit;
+
+       /**
+        * HashCacheItem constructor.
+        * @param $key
+        * @param $value
+        * @param $hit
+        */
+       public function __construct( $key, $value, $hit ) {
+               $this->key = $key;
+               $this->value = $value;
+               $this->hit = $hit;
+       }
+
+       /**
+        * Returns the key for the current cache item.
+        *
+        * The key is loaded by the Implementing Library, but should be 
available to
+        * the higher level callers when needed.
+        *
+        * @return string
+        *   The key string for this cache item.
+        */
+       public function getKey() {
+               return $this->key;
+       }
+
+       /**
+        * Retrieves the value of the item from the cache associated with this 
object's key.
+        *
+        * The value returned must be identical to the value originally stored 
by set().
+        *
+        * If isHit() returns false, this method MUST return null. Note that 
null
+        * is a legitimate cached value, so the isHit() method SHOULD be used to
+        * differentiate between "null value was found" and "no value was 
found."
+        *
+        * @return mixed
+        *   The value corresponding to this cache item's key, or null if not 
found.
+        */
+       public function get() {
+               return $this->value;
+       }
+
+       /**
+        * Confirms if the cache item lookup resulted in a cache hit.
+        *
+        * Note: This method MUST NOT have a race condition between calling 
isHit()
+        * and calling get().
+        *
+        * @return bool
+        *   True if the request resulted in a cache hit. False otherwise.
+        */
+       public function isHit() {
+               return $this->hit;
+       }
+
+       /**
+        * Sets the value represented by this cache item.
+        *
+        * The $value argument may be any item that can be serialized by PHP,
+        * although the method of serialization is left up to the Implementing
+        * Library.
+        *
+        * @param mixed $value
+        *   The serializable value to be stored.
+        *
+        * @return static
+        *   The invoked object.
+        */
+       public function set( $value ) {
+               $this->value = $value;
+       }
+
+       /**
+        * Sets the expiration time for this cache item.
+        *
+        * @param \DateTimeInterface|null $expiration
+        *   The point in time after which the item MUST be considered expired.
+        *   If null is passed explicitly, a default value MAY be used. If none 
is set,
+        *   the value should be stored permanently or for as long as the
+        *   implementation allows.
+        *
+        * @return static
+        *   The called object.
+        */
+       public function expiresAt( $expiration ) {
+               // TODO: Implement expiresAt() method.
+       }
+
+       /**
+        * Sets the expiration time for this cache item.
+        *
+        * @param int|\DateInterval|null $time
+        *   The period of time from the present after which the item MUST be 
considered
+        *   expired. An integer parameter is understood to be the time in 
seconds until
+        *   expiration. If null is passed explicitly, a default value MAY be 
used.
+        *   If none is set, the value should be stored permanently or for as 
long as the
+        *   implementation allows.
+        *
+        * @return static
+        *   The called object.
+        */
+       public function expiresAfter( $time ) {
+               // TODO: Implement expiresAfter() method.
+       }
+}
diff --git a/PaymentProviders/Ingenico/BankPaymentProvider.php 
b/PaymentProviders/Ingenico/BankPaymentProvider.php
index 9c0891a..8803ae9 100644
--- a/PaymentProviders/Ingenico/BankPaymentProvider.php
+++ b/PaymentProviders/Ingenico/BankPaymentProvider.php
@@ -5,7 +5,8 @@
 /**
  * Handle bank payments via Ingenico
  * Will eventually implement PaymentProvider, but right now just looks
- * up iDEAL banks
+ * up iDEAL banks. Caches the results in the PSR-6 cache defined at
+ * config key 'cache'.
  */
 class BankPaymentProvider extends IngenicoPaymentProvider {
 
@@ -19,21 +20,36 @@
         * @return array Keys are bank codes, values are names
         */
        public function getBankList( $country, $currency, $productId = 809 ) {
-               $query = array(
-                       'countryCode' => $country,
-                       'currencyCode' => $currency
-               );
-               $path = "products/$productId/directory";
-               $response = $this->makeApiCall( $path, 'GET', $query );
+               $cacheKey = $this->makeCacheKey( $country, $currency, 
$productId );
+               $cache = $this->config->object( 'cache' );
+               $cacheItem = $cache->getItem( $cacheKey );
 
-               // TODO: base class should probably decode
-               $decoded = json_decode( $response['body'] );
+               if ( !$cacheItem->isHit() ) {
+                       $query = array(
+                               'countryCode' => $country,
+                               'currencyCode' => $currency
+                       );
+                       $path = "products/$productId/directory";
+                       $response = $this->makeApiCall( $path, 'GET', $query );
 
-               $banks = array();
+                       // TODO: base class should probably decode
+                       $decoded = json_decode( $response['body'] );
 
-               foreach( $decoded->entries as $entry ) {
-                       $banks[$entry->issuerId] = $entry->issuerName;
+                       $banks = array();
+
+                       foreach ( $decoded->entries as $entry ) {
+                               $banks[$entry->issuerId] = $entry->issuerName;
+                       }
+                       $cacheItem->set( $banks );
+                       $duration = $this->config->val( 'bank-cache/duration' );
+                       $cacheItem->expiresAfter( $duration );
+                       $cache->save( $cacheItem );
                }
-               return $banks;
+               return $cacheItem->get();
+       }
+
+       protected function makeCacheKey( $country, $currency, $productId ) {
+               $base = $this->config->val( 'bank-cache/key' );
+               return "{$base}_{$country}_{$currency}_{$productId}";
        }
 }
diff --git 
a/PaymentProviders/Ingenico/Tests/phpunit/BankPaymentProviderTest.php 
b/PaymentProviders/Ingenico/Tests/phpunit/BankPaymentProviderTest.php
index 1db0a84..bf3d702 100644
--- a/PaymentProviders/Ingenico/Tests/phpunit/BankPaymentProviderTest.php
+++ b/PaymentProviders/Ingenico/Tests/phpunit/BankPaymentProviderTest.php
@@ -20,6 +20,7 @@
                $config = $this->setConfig( 'ingenico' );
                $this->curlWrapper = $this->getMock( 
'\SmashPig\Core\Http\CurlWrapper' );
                $config->overrideObjectInstance( 'curl/wrapper', 
$this->curlWrapper );
+               $config->object( 'cache' )->clear();
                parent::setUp();
        }
 
@@ -35,6 +36,22 @@
                );
        }
 
+       public function testCacheBankList() {
+               $this->setUpResponse( 'productDirectory', 200 );
+               $this->curlWrapper->expects( $this->once() )
+                       ->method( 'execute' );
+               $provider = new BankPaymentProvider();
+               $results = $provider->getBankList( 'NL', 'EUR' );
+               $this->assertEquals(
+                       array(
+                               'INGBNL2A' => 'Issuer Simulation V3 - ING'
+                       ),
+                       $results
+               );
+               $cachedResults = $provider->getBankList( 'NL', 'EUR' );
+               $this->assertEquals( $results, $cachedResults );
+       }
+
        protected function setUpResponse( $filename, $statusCode ) {
                $contents = file_get_contents( __DIR__ . 
"/../Data/$filename.response" );
                $parsed = CurlWrapper::parseResponse(
diff --git a/SmashPig.yaml b/SmashPig.yaml
index 581ae1b..2f1db12 100644
--- a/SmashPig.yaml
+++ b/SmashPig.yaml
@@ -156,6 +156,12 @@
         retries: 3
         user-agent: SmashPig
 
+    # Must implement Psr\Cache\CacheItemPoolInterface
+    # See PSR-6: http://www.php-fig.org/psr/psr-6/
+    cache:
+        class: SmashPig\Core\Cache\HashCache
+
+
 adyen:
     logging:
         root-context: SmashPig-Adyen
@@ -447,3 +453,7 @@
     authenticator:
         class: SmashPig\PaymentProviders\Ingenico\Authenticator
         constructor-parameters: []
+
+    bank-cache:
+        key: SMASHPIG_INGENICO_IDEAL_BANK_LIST
+        duration: 300
diff --git a/composer.json b/composer.json
index ead77ea..85b7ee2 100644
--- a/composer.json
+++ b/composer.json
@@ -30,7 +30,8 @@
         "symfony/event-dispatcher": "^2.1",
         "symfony/http-foundation": "^2.1",
         "symfony/yaml": "^2.8",
-        "amzn/login-and-pay-with-amazon-sdk-php": "dev-master"
+        "amzn/login-and-pay-with-amazon-sdk-php": "dev-master",
+        "psr/cache": "^1.0"
     },
        "require-dev": {
                "jakub-onderka/php-parallel-lint": "^0.9",
diff --git a/composer.lock b/composer.lock
index c438192..fb523f3 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
         "Read more about it at 
https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file";,
         "This file is @generated automatically"
     ],
-    "hash": "b9fb8f640ad664864e5c9ea93dbc8b1a",
-    "content-hash": "6f0c1657143a0d54fdac09cb749ba44b",
+    "hash": "ec39d762470b5844acb8b7cc5e9fa91e",
+    "content-hash": "f603529753b80b4b5ed3b7ff87d6d176",
     "packages": [
         {
             "name": "amzn/login-and-pay-with-amazon-sdk-php",
@@ -382,6 +382,52 @@
             "time": "2016-06-16 16:22:20"
         },
         {
+            "name": "psr/cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/cache.git";,
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8";,
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/";
+                }
+            ],
+            "description": "Common interface for caching libraries",
+            "keywords": [
+                "cache",
+                "psr",
+                "psr-6"
+            ],
+            "time": "2016-08-06 20:24:11"
+        },
+        {
             "name": "psr/log",
             "version": "1.0.2",
             "source": {

-- 
To view, visit https://gerrit.wikimedia.org/r/337419
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I457fb093c3777406efbfd4821acac47520af60d2
Gerrit-PatchSet: 4
Gerrit-Project: wikimedia/fundraising/SmashPig
Gerrit-Branch: master
Gerrit-Owner: Ejegg <[email protected]>
Gerrit-Reviewer: Cdentinger <[email protected]>
Gerrit-Reviewer: Ejegg <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to