Ejegg has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/364339 )

Change subject: buildRequestArray handles recursion, used for name/value
......................................................................

buildRequestArray handles recursion, used for name/value

Add a new function for building array structures for API requests,
similar to the XML builders.

Change-Id: I9f0775720408a0eff685cd311295c33e327c4b01
---
M gateway_common/ArrayHelper.php
M gateway_common/gateway.adapter.php
A tests/phpunit/ArrayHelperTest.php
3 files changed, 233 insertions(+), 39 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DonationInterface 
refs/changes/39/364339/1

diff --git a/gateway_common/ArrayHelper.php b/gateway_common/ArrayHelper.php
index 7256e91..c0cece8 100644
--- a/gateway_common/ArrayHelper.php
+++ b/gateway_common/ArrayHelper.php
@@ -19,5 +19,59 @@
 
                return array_combine( $keys, $values );
        }
+
+       /**
+        * Create an array mirroring the given structure, but with values 
obtained
+        * from a callback. Like array_map, but with a few differences
+        *  - recurses when elements are themselves arrays
+        *  - if the callback returns '' or false, the element is skipped in 
the output
+        *  - if the element is a key with value null, the output element has 
no key
+        *
+        * @param callable $callback
+        * @param array $structure
+        * @return array
+        */
+       public static function buildRequestArray( $callback, $structure ) {
+               $data = array();
+               foreach ( $structure as $key => $value ) {
+                       self::addArrayElement( $data, $structure, $key, $value, 
$callback );
+               }
+               return $data;
+       }
+
+       protected static function addArrayElement( &$targetElement, 
$structureElement, $key, $value, $callback ) {
+               if ( is_numeric( $key ) ) {
+                       // it's just a value, not an associative array key
+                       $fieldName = $value;
+                       $fieldValue = $callback( $fieldName );
+                       if ( self::includeElement( $fieldValue ) ) {
+                               $targetElement[$fieldName] = $fieldValue;
+                       }
+                       return;
+               }
+               if ( is_array( $structureElement[$key] ) ) {
+                       $targetElement[$key] = array();
+                       foreach ( $structureElement[$key] as $subKey => 
$subValue ) {
+                               self::addArrayElement(
+                                       $targetElement[$key],
+                                       $structureElement[$key],
+                                       $subKey,
+                                       $subValue,
+                                       $callback
+                               );
+                       }
+                       // TODO: If all children are skipped, remove 
$targetElement[$key] ?
+               } else if ( $structureElement[$key] === null ) {
+                       // HACK: needed a way to specify non-associative arrays 
in the output
+                       $fieldValue = $callback( $key );
+                       if ( self::includeElement( $fieldValue ) ) {
+                               $targetElement[] = $fieldValue;
+                       }
+               }
+       }
+
+       protected static function includeElement( $value ) {
+               return ( $value !== '' && $value !== false );
+       }
 }
 
diff --git a/gateway_common/gateway.adapter.php 
b/gateway_common/gateway.adapter.php
index e34988d..5c73969 100644
--- a/gateway_common/gateway.adapter.php
+++ b/gateway_common/gateway.adapter.php
@@ -742,22 +742,19 @@
         * curl'd off to the remote server.
         */
        protected function buildRequestNameValueString() {
+               $data = $this->buildRequestArray();
+               $ret = http_build_query( $data );
+               return $ret;
+       }
+
+       protected function buildRequestArray() {
                // Look up the request structure for our current transaction 
type in the transactions array
                $structure = $this->getTransactionRequestStructure();
                if ( !is_array( $structure ) ) {
-                       return '';
+                       return array();
                }
-
-               $data = array();
-               foreach ( $structure as $fieldname ) {
-                       $fieldvalue = $this->getTransactionSpecificValue( 
$fieldname );
-                       if ( $fieldvalue !== '' && $fieldvalue !== false ) {
-                               $data[$fieldname] = $fieldvalue;
-                       }
-               }
-
-               $ret = http_build_query( $data );
-               return $ret;
+               $callback = array( $this, 'getTransactionSpecificValue' );
+               return ArrayHelper::buildRequestArray( $callback, $structure );
        }
 
        /**
@@ -1002,40 +999,45 @@
                                // TODO: Maybe move this to the pre_process 
functions?
                                $this->dataObj->saveContributionTrackingData();
                        }
-
                        $commType = $this->getCommunicationType();
-                       if ( $commType === 'redirect' ) {
+                       switch( $commType ) {
+                               case 'redirect':
 
-                               //in the event that we have a redirect 
transaction that never displays the form,
-                               //save this most recent one before we leave.
-                               $this->session_pushFormName( 
$this->getData_Unstaged_Escaped( 'ffname' ) );
+                                       //in the event that we have a redirect 
transaction that never displays the form,
+                                       //save this most recent one before we 
leave.
+                                       $this->session_pushFormName( 
$this->getData_Unstaged_Escaped( 'ffname' ) );
 
-                               
$this->transaction_response->setCommunicationStatus( true );
+                                       
$this->transaction_response->setCommunicationStatus( true );
 
-                               // Build the redirect URL.
-                               $redirectUrl = $this->getProcessorUrl();
-                               $redirectParams = $this->buildRequestParams();
-                               if ( $redirectParams ) {
-                                       // Add GET parameters, if provided.
-                                       $redirectUrl .= '?' . http_build_query( 
$redirectParams );
-                               }
+                                       // Build the redirect URL.
+                                       $redirectUrl = $this->getProcessorUrl();
+                                       $redirectParams = 
$this->buildRequestParams();
+                                       if ( $redirectParams ) {
+                                               // Add GET parameters, if 
provided.
+                                               $redirectUrl .= '?' . 
http_build_query( $redirectParams );
+                                       }
 
-                               $this->transaction_response->setRedirect( 
$redirectUrl );
+                                       
$this->transaction_response->setRedirect( $redirectUrl );
 
-                               return $this->transaction_response;
+                                       return $this->transaction_response;
 
-                       } elseif ( $commType === 'xml' ) {
-                               $this->profiler->getStopwatch( 
"buildRequestXML", true ); // begin profiling
-                               $curlme = $this->buildRequestXML(); // build 
the XML
-                               $this->profiler->saveCommunicationStats( 
"buildRequestXML", $transaction ); // save profiling data
-
-                       } elseif ( $commType === 'namevalue' ) {
-                               $this->profiler->getStopwatch( 
"buildRequestNameValueString", true ); // begin profiling
-                               $curlme = $this->buildRequestNameValueString(); 
// build the name/value pairs
-                               $this->profiler->saveCommunicationStats( 
"buildRequestNameValueString", $transaction ); // save profiling data
-
-                       } else {
-                               throw new UnexpectedValueException( 
"Communication type of '{$commType}' unknown" );
+                               case 'xml':
+                                       $this->profiler->getStopwatch( 
"buildRequestXML", true ); // begin profiling
+                                       $curlme = $this->buildRequestXML(); // 
build the XML
+                                       
$this->profiler->saveCommunicationStats( "buildRequestXML", $transaction ); // 
save profiling data
+                                       break;
+                               case 'namevalue':
+                                       $this->profiler->getStopwatch( 
"buildRequestNameValueString", true ); // begin profiling
+                                       $curlme = 
$this->buildRequestNameValueString(); // build the name/value pairs
+                                       
$this->profiler->saveCommunicationStats( "buildRequestNameValueString", 
$transaction );
+                                       break;
+                               case 'array':
+                                       $this->profiler->getStopwatch( 
"buildRequestNameValueString", true ); // begin profiling
+                                       $curlme = $this->buildRequestArray(); 
// build the name/value pairs
+                                       
$this->profiler->saveCommunicationStats( "buildRequestNameValueString", 
$transaction );
+                                       break;
+                               default:
+                                       throw new UnexpectedValueException( 
"Communication type of '{$commType}' unknown" );
                        }
                } catch ( Exception $e ) {
                        $this->logger->critical( 'Malformed gateway definition. 
Cannot continue: Aborting.\n' . $e->getMessage() );
diff --git a/tests/phpunit/ArrayHelperTest.php 
b/tests/phpunit/ArrayHelperTest.php
new file mode 100644
index 0000000..979fd28
--- /dev/null
+++ b/tests/phpunit/ArrayHelperTest.php
@@ -0,0 +1,138 @@
+<?php
+/**
+ * Wikimedia Foundation
+ *
+ * LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * @group      Fundraising
+ * @group      DonationInterface
+ * @group   ArrayHelper
+ */
+class ArrayHelperTest extends PHPUnit_Framework_TestCase {
+
+       /**
+        * @dataProvider getTestData
+        */
+       public function testBuildRequestArray( $description, $input, $expected 
) {
+               $actual = ArrayHelper::buildRequestArray( $this->getCallback(), 
$input );
+               $message = "buildRequestArray failed for $description";
+               $this->assertEquals( $expected, $actual, $message );
+       }
+
+       public function getTestData() {
+               return array(
+                       // 1st test case
+                       array(
+                               'flat array',
+                               // input
+                               array(
+                                       'double',
+                                       'toil',
+                                       'trouble'
+                               ),
+                               // output
+                               array(
+                                       'double' => 'value for -double-',
+                                       'toil' => 'value for -toil-',
+                                       'trouble' => 'value for -trouble-',
+                               ),
+                       ),
+                       // 2nd test case
+                       array(
+                               'nested array',
+                               // input
+                               array(
+                                       'fire' => array(
+                                               'burn',
+                                       ),
+                                       'cauldron' => array(
+                                               'bubble',
+                                       )
+                               ),
+                               // output
+                               array(
+                                       'fire' => array(
+                                               'burn' => 'value for -burn-',
+                                       ),
+                                       'cauldron' => array(
+                                               'bubble' => 'value for 
-bubble-',
+                                       )
+                               ),
+                       ),
+                       // 3rd test case
+                       array(
+                               'mixed array',
+                               // input
+                               array(
+                                       'poisoned entrails',
+                                       'toad' => array(
+                                               'under_cold_stone',
+                                               'days_and_nights',
+                                               'thirty_one'
+                                       ),
+                               ),
+                               // output
+                               array(
+                                       'poisoned entrails' => 'value for 
-poisoned entrails-',
+                                       'toad' => array(
+                                               'under_cold_stone' => 'value 
for -under_cold_stone-',
+                                               'days_and_nights' => 'value for 
-days_and_nights-',
+                                               'thirty_one' => 'value for 
-thirty_one-'
+                                       )
+                               ),
+                       ),
+                       // 4th test case
+                       array(
+                               'omit empty',
+                               // input
+                               array(
+                                       'eye_of_newt',
+                                       'toe_of_frog',
+                                       'wool_of_bat', // callback returns '' 
for this value
+                                       'tongue_of_dog',
+                               ),
+                               // output
+                               array(
+                                       'eye_of_newt' => 'value for 
-eye_of_newt-',
+                                       'toe_of_frog' => 'value for 
-toe_of_frog-',
+                                       'tongue_of_dog' => 'value for 
-tongue_of_dog-'
+                               ),
+                       ),
+                       // 5th test case
+                       array(
+                               'non-associative output',
+                               // input
+                               array(
+                                       'scale_of_dragon' => null,
+                                       'tooth_of_wolf' => null
+                               ),
+                               // output
+                               array(
+                                       'value for -scale_of_dragon-',
+                                       'value for -tooth_of_wolf-'
+                               ),
+                       )
+               );
+       }
+
+       protected function getCallback() {
+               return function( $key ) {
+                       if ( $key === 'wool_of_bat' ) {
+                               return '';
+                       }
+                       return "value for -$key-";
+               };
+       }
+}

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I9f0775720408a0eff685cd311295c33e327c4b01
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/DonationInterface
Gerrit-Branch: master
Gerrit-Owner: Ejegg <ej...@ejegg.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to