r107551 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r107550‎ | r107551 | r107552 >
Date:02:01, 29 December 2011
Author:khorn
Status:deferred (Comments)
Tags:fundraising 
Comment:
DataValidator class, initial commit.
This class of static functions is designed to be used for data validation in DonationInterface, no matter where it may be required.
It is also capable of generating all the translated form error messages.
Modified paths:
  • /trunk/extensions/DonationInterface/gateway_common/DataValidator.php (added) (history)

Diff [purge]

Index: trunk/extensions/DonationInterface/gateway_common/DataValidator.php
@@ -0,0 +1,676 @@
 2+<?php
 3+
 4+/**
 5+ * DataValidator
 6+ * This class is responsible for performing all kinds of data validation,
 7+ * wherever we may need it.
 8+ *
 9+ * All functions should be static, so we don't have to construct anything in
 10+ * order to use it any/everywhere.
 11+ *
 12+ * @author khorn
 13+ */
 14+class DataValidator {
 15+
 16+ /**
 17+ * $boolean_fields
 18+ * @var array All fields that should validate as boolean values
 19+ */
 20+ protected static $boolean_fields = array(
 21+ 'comment-option',
 22+ 'email-opt',
 23+ '_cache_',
 24+ 'anonymous',
 25+ 'optout',
 26+ 'recurring',
 27+ 'posted',
 28+ );
 29+
 30+ /**
 31+ * $numeric_fields
 32+ * @var array All fields that should validate as numeric
 33+ */
 34+ protected static $numeric_fields = array(
 35+ 'amount',
 36+ 'amountGiven',
 37+ 'amountOther',
 38+ 'cvv',
 39+ 'contribution_tracking_id',
 40+ 'account_number',
 41+ 'expiration',
 42+ 'order_id',
 43+ 'i_order_id',
 44+ 'numAttempt'
 45+ );
 46+
 47+ /**
 48+ * $gateway_classes
 49+ * @var array A list of all possible gateway classes.
 50+ */
 51+ protected static $gateway_classes = array(
 52+ 'globalcollect' => 'GlobalCollectAdapter',
 53+ 'payflowpro' => 'PayflowProAdapter'
 54+ );
 55+
 56+ /**
 57+ * $card_types
 58+ * @var array A list of all card types we recognize
 59+ */
 60+ protected static $card_types = array(
 61+ 'american',
 62+ 'mastercard',
 63+ 'visa',
 64+ 'discover'
 65+ );
 66+
 67+
 68+ /**
 69+ * getNumericFields returns a list of DonationInterface fields that are
 70+ * expected to contain numeric values.
 71+ * @return array A non-ordered array of field names.
 72+ */
 73+ public static function getNumericFields(){
 74+ return self::$numeric_fields;
 75+ }
 76+
 77+
 78+ /**
 79+ * getBooleanFields returns a list of DonationInterface fields that are
 80+ * expected to contain boolean values.
 81+ * @return array A non-ordered array of field names.
 82+ */
 83+ public static function getBooleanFields(){
 84+ return self::$boolean_fields;
 85+ }
 86+
 87+
 88+ /**
 89+ * getErrorToken, intended to be used by classes that exist relatively close
 90+ * to the form classes, returns the error token (defined on the forms) that
 91+ * specifies *where* the error will appear within the form output.
 92+ * @param string $field The field that ostensibly has an error that needs to
 93+ * be displayed to the user.
 94+ * @return string The error token corresponding to a field, probably in
 95+ * RapidHTML.
 96+ */
 97+ public static function getErrorToken( $field ){
 98+ $error_token = 'general';
 99+ switch ( $field ) {
 100+ case 'amountGiven' :
 101+ case 'amountOther' :
 102+ $error_token = 'amount';
 103+ break;
 104+ case 'email' :
 105+ $error_token = 'emailAdd';
 106+ break;
 107+ case 'amount' :
 108+ case 'card_num':
 109+ case 'card_type':
 110+ case 'cvv':
 111+ case 'fname':
 112+ case 'lname':
 113+ case 'city':
 114+ case 'country':
 115+ case 'street':
 116+ case 'state':
 117+ case 'zip':
 118+ $error_token = $field;
 119+ break;
 120+ }
 121+ return $error_token;
 122+ }
 123+
 124+
 125+ /**
 126+ * getErrorMessage - returns the translated error message appropriate for a
 127+ * validation error on the specified field, of the specified type.
 128+ * @param string $field - The common name of the field containing the data
 129+ * that is causing the error.
 130+ * @param type $type - The type of error being caused, from a set.
 131+ * Possible values are:
 132+ * 'non_empty' - the value is required and not currently present
 133+ * 'valid_type' - in general, the wrong format
 134+ * 'calculated' - fields that failed some kind of multiple-field data
 135+ * integrity check.
 136+ * @param string $value - The value of the field. So far, only used to say
 137+ * more precise things about Credit Cards.
 138+ */
 139+ public static function getErrorMessage( $field, $type, $value = null ){
 140+ //this is gonna get ugly up in here.
 141+ error_log( __FUNCTION__ . " $field, $type, $value " );
 142+
 143+ //Empty messages should get:
 144+ //'donate_interface-error-msg' => 'Please enter your $1';
 145+ //If they have no defined error message, give 'em the default.
 146+ if ($type === 'non_empty'){
 147+ //NOTE: We are just using the next bit because it's convenient.
 148+ //getErrorToken is actually for something entirely different:
 149+ //Figuring out where on the form the error should land.
 150+ $message_field = self::getErrorToken( $field );
 151+ if ( $field === 'expiration' ){
 152+ $message_field = $field;
 153+ }
 154+ //postal code is a weird one. More L10n than I18n.
 155+ //'donate_interface-error-msg-postal' => 'postal code',
 156+
 157+ $error_message_field_string = 'donate_interface-error-msg-' . $message_field;
 158+ if ( $message_field != 'general' && self::wmfMessageExists( $error_message_field_string ) ) {
 159+ return wfMsg( 'donate_interface-error-msg', wfMsg( $error_message_field_string ) );
 160+ }
 161+ }
 162+
 163+ if ( $type === 'valid_type' ) {
 164+ //NOTE: We are just using the next bit because it's convenient.
 165+ //getErrorToken is actually for something entirely different:
 166+ //Figuring out where on the form the error should land.
 167+ $token = self::getErrorToken( $field );
 168+ $suffix = $token; //defaultness
 169+ switch ($token){
 170+ case 'amount':
 171+ $suffix = 'invalid-amount';
 172+ case 'emailAdd':
 173+ $suffix = 'email';
 174+ case 'card_num': //god damn it.
 175+ $suffix = 'card_num'; //more defaultness.
 176+ if (!is_null($value)){
 177+ $fake_suffix = self::getCardType($value);
 178+ switch ( $fake_suffix ){
 179+ case 'american':
 180+ $suffix = 'amex';
 181+ break;
 182+ case 'mastercard':
 183+ $suffix = 'mc';
 184+ break;
 185+ case 'visa':
 186+ case 'discover':
 187+ $suffix = $fake_suffix;
 188+ break;
 189+ }
 190+ }
 191+ break;
 192+ }
 193+
 194+ $error_message_field_string = 'donate_interface-error-msg-' . $suffix;
 195+ if ( self::wmfMessageExists( $error_message_field_string ) ) {
 196+ return wfMsg( $error_message_field_string );
 197+ }
 198+ }
 199+
 200+ //ultimate defaultness.
 201+ return wfMsg( 'donate_interface-error-msg-general' );
 202+ }
 203+
 204+
 205+ /**
 206+ * wmfMessageExists returns true if a translatable message has been defined
 207+ * for the string that has been passed in, false if none is present.
 208+ * TODO: See what this does in other languages when the string exists in
 209+ * English, but not in the uselang.
 210+ * @param string $msg_key The message string to look up.
 211+ * @return boolean - true if message, exists, otherwise false.
 212+ */
 213+ public static function wmfMessageExists( $msg_key ){
 214+ //we may have some problems here if this returns false positives if a
 215+ //message exists in English, but not in the language we're looking for.
 216+ //..but, they're not problems we don't already have.
 217+ if ( wfEmptyMsg( $msg_key ) ) {
 218+ return false;
 219+ } else {
 220+ return true;
 221+ }
 222+ }
 223+
 224+
 225+ /**
 226+ * validate
 227+ * Run all the validation rules we have defined against a (hopefully
 228+ * normalized) DonationInterface data set.
 229+ * @param array $data The DonationInterface data set, or a subset thereof.
 230+ * @param array $check_not_empty An array of fields to do empty validation
 231+ * on. If this is not populated, no fields will throw errors for being empty,
 232+ * UNLESS they are required for a field that uses them for more complex
 233+ * validation (the 'calculated' phase).
 234+ * @return array An array of errors in a format ready for any derivitive of
 235+ * the main DonationInterface Form class to display. The array will be empty
 236+ * if no errors were generated and everything passed OK.
 237+ */
 238+ public static function validate( $data, $check_not_empty = array() ){
 239+ //return the array of errors that should be generated on validate.
 240+ //just the same way you'd do it if you were a form passing the error array around.
 241+
 242+ /**
 243+ * We need to run the validation in an order that makes sense.
 244+ *
 245+ * First: If we need to validate that some things are not empty, do that.
 246+ * Second: Do regular data type validation (on things that are not empty,
 247+ * keeping in mind we may or may not have exploded on those yet.
 248+ * Third: Do validation that depends on multiple fields (making sure you
 249+ * validated that all the required fields exist on step 1, regardless of
 250+ * $check_not_empty)
 251+ *
 252+ * So, we need to know what we're about to do for #3 before we actually do #1.
 253+ *
 254+ * $check_not_empty should contain an array of values that need to be populated.
 255+ * One likely candidate for a source there, is the required stomp fields as defined in DonationData.
 256+ * Although, a lot of those don't have to have any data in them either. Boo.
 257+ *
 258+ * How about we build an array of shit to do,
 259+ * look at it to make sure it's complete, and in order...
 260+ * ...and do it.
 261+ */
 262+
 263+ $instructions = array(
 264+ 'non_empty' => array(),
 265+ 'valid_type' => array(), //simple 'valid_type' check functions only have one parameter.
 266+ 'calculated' => array(), //'calculated' check functions depend on (or optionally have) more than one value.
 267+ );
 268+
 269+ if ( !is_array( $check_not_empty ) ){
 270+ $check_not_empty = array( $check_not_empty );
 271+ }
 272+
 273+ foreach ( $check_not_empty as $field ){
 274+ $instructions['non_empty'][$field] = 'validate_not_empty';
 275+ }
 276+
 277+ foreach ( $data as $field => $value ){
 278+ //first, unset everything that's an empty string, or null, as there's nothing to validate.
 279+ if ( $value !== '' && !is_null( $value ) ){
 280+
 281+ $function_name = self::getValidationFunction( $field );
 282+ $check_type = 'valid_type';
 283+ switch ( $function_name ) {
 284+ case 'validate_amount':
 285+ //Note: We could do something like also validate amount not empty, and then that it's numeric
 286+ //That way we'd get more precisely granular error messages.
 287+ $check_type = 'calculated';
 288+ $instructions['non_empty']['currency_code'] = 'validate_not_empty';
 289+ $instructions['valid_type']['currency_code'] = self::getValidationFunction( 'currency_code' );
 290+ $instructions['non_empty']['gateway'] = 'validate_not_empty';
 291+ $instructions['valid_type']['gateway'] = self::getValidationFunction( 'gateway' );
 292+ break;
 293+ case 'validate_card_type':
 294+ $check_type = 'calculated';
 295+ break;
 296+ }
 297+ $instructions[$check_type][$field] = $function_name;
 298+ }
 299+ }
 300+
 301+ $errors = array();
 302+
 303+ $self = get_called_class();
 304+
 305+ foreach ( $instructions['non_empty'] as $field => $function ){
 306+ if ( method_exists( $self, $function ) && $function === 'validate_not_empty' ) {
 307+ if ( $self::$function( $field, $data ) ){
 308+ $instructions['non_empty'][$field] = true;
 309+ } else {
 310+ $instructions['non_empty'][$field] = false;
 311+ $errors[ self::getErrorToken( $field ) ] = self::getErrorMessage( $field, 'non_empty' );
 312+ }
 313+ } else {
 314+ $instructions['non_empty'][$field] === 'exception';
 315+ $errors[ self::getErrorToken( $field ) ] = self::getErrorMessage( $field, 'non_empty' );
 316+ throw new MWException( __FUNCTION__ . " BAD PROGRAMMER. No $function function. ('non_empty' rule for $field )" );
 317+ }
 318+ }
 319+
 320+ foreach ( $instructions['valid_type'] as $field => $function ){
 321+ if ( method_exists( $self, $function ) ) {
 322+ if ( $self::$function( $data[$field] ) ){
 323+ $instructions['valid_type'][$field] = true;
 324+ } else {
 325+ $instructions['valid_type'][$field] = false;
 326+ $errors[ self::getErrorToken( $field ) ] = self::getErrorMessage( $field, 'valid_type' );
 327+ }
 328+ } else {
 329+ $instructions['valid_type'][$field] === 'exception';
 330+ $errors[ self::getErrorToken( $field ) ] = self::getErrorMessage( $field, 'valid_type' );
 331+ throw new MWException( __FUNCTION__ . " BAD PROGRAMMER. No $function function. ('valid_type' rule for $field)" );
 332+ }
 333+ }
 334+
 335+ //don't bail out now. Just don't set errors for calculated fields that
 336+ //have failures in their dependencies.
 337+ foreach ( $instructions['calculated'] as $field => $function ){
 338+ if ( method_exists( $self, $function ) ) {
 339+ //each of these is going to have its own set of overly
 340+ //complicated rules and things to check, or we wouldn't be down
 341+ //here in the calculated section.
 342+ $result = null;
 343+ switch ( $function ){
 344+ case 'validate_amount':
 345+ if ( self::checkValidationPassed( array( 'currency_code', 'gateway' ), $instructions ) ){
 346+ $result = $self::$function( $data[$field], $data['currency_code'], $data['gateway'] );
 347+ } //otherwise, just don't do the validation. The other stuff will be complaining already.
 348+ break;
 349+ case 'validate_card_type':
 350+ //the contingent field in this case isn't strictly required, so this is going to look funny.
 351+ if ( array_key_exists( 'card_number', $instructions['valid_type'] ) && $instructions['valid_type']['card_number'] === true ){
 352+ //if it's there, it had better match up.
 353+ $result = $self::$function( $data[$field], $data['card_number'] );
 354+ } else {
 355+ $result = $self::$function( $data[$field] );
 356+ }
 357+ break;
 358+ }
 359+
 360+ $instructions['calculated'][$field] = $result;
 361+ if ($result === false){ //implying we did the check, and it failed.
 362+ $errors[ self::getErrorToken( $field ) ] = self::getErrorMessage( $field, 'calculated' );
 363+ }
 364+
 365+ } else {
 366+ $instructions['calculated'][$field] === 'exception';
 367+ $errors[ self::getErrorToken( $field ) ] = self::getErrorMessage( $field, 'calculated' );
 368+ throw new MWException( __FUNCTION__ . " BAD PROGRAMMER. No $function function. ('calculated' rule for $field)" );
 369+ }
 370+ }
 371+ error_log( print_r( $errors, true ) );
 372+ return $errors;
 373+ }
 374+
 375+
 376+ /**
 377+ * checkValidationPassed is a validate helper function.
 378+ * In order to determine that we are ready to do the third stage of data
 379+ * validation (calculated) for any given field, we need to determine that
 380+ * all fields required to validate the original have, themselves, passed
 381+ * validation.
 382+ * @param array $fields An array of field names to check.
 383+ * @param array $instruction_results The $instructions array used in the
 384+ * validate function.
 385+ * @return boolean true if all fields specified in $fields passed their
 386+ * non_empty and valid_type validation. Otherwise, false.
 387+ */
 388+ protected static function checkValidationPassed( $fields, $instruction_results ){
 389+ foreach ( $fields as $field ){
 390+ if ( !array_key_exists( $field, $instruction_results['non_empty'] ) || $instruction_results['non_empty'][$field] !== true ){
 391+ return false;
 392+ }
 393+ if ( !array_key_exists( $field, $instruction_results['valid_type'] ) || $instruction_results['valid_type'][$field] !== true ){
 394+ return false;
 395+ }
 396+ }
 397+ return true;
 398+ }
 399+
 400+
 401+ /**
 402+ * getValidationFunction returns the function to use to validate the given field.
 403+ * @param string $field The name of the field we need to validate.
 404+ */
 405+ static function getValidationFunction( $field ){
 406+ switch ( $field ){
 407+ case 'email':
 408+ return 'validate_email';
 409+ break;
 410+ case 'amount': //we only have to do the one: It will have been normalized by now.
 411+ return 'validate_amount'; //this one is interesting. Needs two params.
 412+ break;
 413+ case 'card_num':
 414+ return 'validate_credit_card';
 415+ break;
 416+ case 'card_type':
 417+ return 'validate_card_type';
 418+ break;
 419+ case 'gateway':
 420+ return 'validate_gateway';
 421+ break;
 422+ }
 423+
 424+ if ( in_array( $field, self::getNumericFields() ) ){
 425+ return 'validate_numeric';
 426+ }
 427+
 428+ if ( in_array( $field, self::getBooleanFields() ) ){
 429+ return 'validate_boolean';
 430+ }
 431+
 432+ return 'validate_alphanumeric'; //Yeah, this won't work.
 433+ }
 434+
 435+
 436+ /**
 437+ * validate_email
 438+ * Determines if the $value passed in is a valid email address.
 439+ * @param string $value The piece of data that is supposed to be an email
 440+ * address.
 441+ * @return boolean True if $value is a valid email address, otherwise false.
 442+ */
 443+ protected static function validate_email( $value ){
 444+ // is email address valid?
 445+ $isEmail = User::isValidEmailAddr( $value );
 446+ return $isEmail;
 447+ }
 448+
 449+
 450+ /**
 451+ * validate_amount
 452+ * Determines if the $value passed in is a valid amount.
 453+ * NOTE: You will need to make sure that currency_code is populated before
 454+ * you get here.
 455+ * @param string $value The piece of data that is supposed to be an amount.
 456+ * @param string $currency_code Valid amounts depend on there being a
 457+ * currency code also. This also needs to be passed in.
 458+ * @param string $gateway The gateway needs to be provided so we can
 459+ * determine that gateway's current price floor and ceiling.
 460+ * @return boolean True if $value is a valid amount, otherwise false.
 461+ */
 462+ protected static function validate_amount( $value, $currency_code, $gateway ){
 463+ if ( !$value || !$currency_code || !is_numeric( $value ) ) {
 464+ return false;
 465+ }
 466+
 467+ // check amount
 468+ $gateway_class = self::getGatewayClass($gateway);
 469+ if ( !$gateway_class ){
 470+ return false;
 471+ }
 472+
 473+ $priceFloor = $gateway_class::getGlobal( 'PriceFloor' );
 474+ $priceCeiling = $gateway_class::getGlobal( 'PriceCeiling' );
 475+ if ( !preg_match( '/^\d+(\.(\d+)?)?$/', $value ) ||
 476+ ( ( float ) self::convert_to_usd( $currency_code, $value ) < ( float ) $priceFloor ||
 477+ ( float ) self::convert_to_usd( $currency_code, $value ) > ( float ) $priceCeiling ) ) {
 478+ return false;
 479+ }
 480+
 481+ return true;
 482+ }
 483+
 484+
 485+ /**
 486+ * validate_card_type
 487+ * Determines if the $value passed in is (possibly) a valid credit card type.
 488+ * @param string $value The piece of data that is supposed to be a credit card type.
 489+ * @param string $card_number The card number associated with this card type. Optional.
 490+ * @return boolean True if $value is a reasonable credit card type, otherwise false.
 491+ */
 492+ protected static function validate_card_type( $value, $card_number = '' ) {
 493+ if ( !array_key_exists( $value, self::$card_types ) ){
 494+ return false;
 495+ }
 496+
 497+ if ( $card_number != '' ){
 498+ $calculated_card_type = self::getCardType( $card_number );
 499+ if ( $calculated_card_type != $value ){
 500+ return false;
 501+ }
 502+ }
 503+
 504+ return true;
 505+ }
 506+
 507+
 508+ /**
 509+ * validate_credit_card
 510+ * Determines if the $value passed in is (possibly) a valid credit card number.
 511+ * @param string $value The piece of data that is supposed to be a credit card number.
 512+ * @return boolean True if $value is a reasonable credit card number, otherwise false.
 513+ */
 514+ protected static function validate_credit_card( $value ) {
 515+ $calculated_card_type = self::getCardType( $value );
 516+ if ( !$calculated_card_type ){
 517+ return false;
 518+ }
 519+
 520+ return true;
 521+ }
 522+
 523+
 524+ /**
 525+ * validate_boolean
 526+ * Determines if the $value passed in is a valid boolean.
 527+ * @param string $value The piece of data that is supposed to be a boolean.
 528+ * @return boolean True if $value is a valid boolean, otherwise false.
 529+ */
 530+ protected static function validate_boolean( $value ){
 531+ switch ($value) {
 532+ case 0:
 533+ case '0':
 534+ case false:
 535+ case 'false':
 536+ case 1:
 537+ case '1':
 538+ case true:
 539+ case 'true':
 540+ return true;
 541+ break;
 542+ }
 543+ return false;
 544+ }
 545+
 546+
 547+ /**
 548+ * validate_numeric
 549+ * Determines if the $value passed in is numeric.
 550+ * @param string $value The piece of data that is supposed to be numeric.
 551+ * @return boolean True if $value is numeric, otherwise false.
 552+ */
 553+ protected static function validate_numeric( $value ){
 554+ //instead of validating here, we should probably be doing something else entirely.
 555+ if ( is_numeric( $value ) ) {
 556+ return true;
 557+ }
 558+ return false;
 559+ }
 560+
 561+
 562+ /**
 563+ * validate_gateway
 564+ * Checks to make sure the gateway is populated with a valid and enabled
 565+ * gateway.
 566+ * @param string $value The value that is meant to be a gateway.
 567+ * @return boolean True if $value is a valid gateway, otherwise false
 568+ */
 569+ protected static function validate_gateway( $value ){
 570+ if ( self::getGatewayClass( $value ) ){
 571+ return true;
 572+ }
 573+
 574+ return false;
 575+ }
 576+
 577+
 578+ /**
 579+ * validate_not_empty
 580+ * Checks to make sure that the $value is present in the $data array, and not null or an empty string.
 581+ * Anything else that is 'falseish' is still perfectly valid to have as a data point.
 582+ * TODO: Consider doing this in a batch.
 583+ * @param string $value The value to check for non-emptyness.
 584+ * @param array $data The whole data set.
 585+ * @return boolean True if the $value is not missing or empty, otherwise false.
 586+ */
 587+ protected static function validate_not_empty( $value, $data ){
 588+ error_log(__FUNCTION__ . ". Yup!");
 589+ if ( !array_key_exists( $value, $data ) || is_null( $data[$value] ) || $data[$value] === '' ){
 590+ return false;
 591+ }
 592+ return true;
 593+ }
 594+
 595+ /**
 596+ * validate_alphanumeric
 597+ * Checks to make sure the value is populated with an alphanumeric value...
 598+ * ...which would be great, if it made sense at all.
 599+ * TODO: This is duuuuumb. Make it do something good, or get rid of it.
 600+ * If we can think of a way to make this useful, we should do something here.
 601+ * @param string $value The value that is meant to be alphanumeric
 602+ * @return boolean True if $value is ANYTHING. Or not. :[
 603+ */
 604+ protected static function validate_alphanumeric( $value ){
 605+ return true;
 606+ }
 607+
 608+
 609+ /**
 610+ * getGatewayClass
 611+ * This exists to enable things like logging to the correct gateway, and
 612+ * retrieving gateway-specific globals.
 613+ * @param string $gateway The gateway identifier.
 614+ * @return string The name of the gateway class associated with that
 615+ * identifier, or false if none exists.
 616+ */
 617+ protected static function getGatewayClass( $gateway ) {
 618+ if ( array_key_exists( $gateway, self::$gateway_classes ) && class_exists( self::$gateway_classes[$gateway] ) ){
 619+ return self::$gateway_classes[$gateway];
 620+ }
 621+ return false;
 622+ }
 623+
 624+
 625+ /**
 626+ * Convert an amount for a particular currency to an amount in USD
 627+ *
 628+ * This is grosley rudimentary and likely wildly inaccurate.
 629+ * This mimicks the hard-coded values used by the WMF to convert currencies
 630+ * for validatoin on the front-end on the first step landing pages of their
 631+ * donation process - the idea being that we can get a close approximation
 632+ * of converted currencies to ensure that contributors are not going above
 633+ * or below the price ceiling/floor, even if they are using a non-US currency.
 634+ *
 635+ * In reality, this probably ought to use some sort of webservice to get real-time
 636+ * conversion rates.
 637+ *
 638+ * @param string $currency_code
 639+ * @param float $amount
 640+ * @return float
 641+ */
 642+ public static function convert_to_usd( $currency_code, $amount ) {
 643+ require_once( dirname( __FILE__ ) . '/currencyRates.inc' );
 644+ $rates = getCurrencyRates();
 645+ $code = strtoupper( $currency_code );
 646+ if ( array_key_exists( $code, $rates ) ) {
 647+ $usd_amount = $amount / $rates[$code];
 648+ } else {
 649+ $usd_amount = $amount;
 650+ }
 651+ return $usd_amount;
 652+ }
 653+
 654+
 655+ /**
 656+ * Calculates and returns the card type for a given credit card number.
 657+ * @param numeric $card_num A credit card number.
 658+ * @return mixed 'american', 'mastercard', 'visa', 'discover', or false.
 659+ */
 660+ public static function getCardType( $card_num ) {
 661+ // validate that credit card number entered is correct and set the card type
 662+ if ( preg_match( '/^3[47][0-9]{13}$/', $card_num ) ) { // american express
 663+ return 'american';
 664+ } elseif ( preg_match( '/^5[1-5][0-9]{14}$/', $card_num ) ) { // mastercard
 665+ return 'mastercard';
 666+ } elseif ( preg_match( '/^4[0-9]{12}(?:[0-9]{3})?$/', $card_num ) ) {// visa
 667+ return 'visa';
 668+ } elseif ( preg_match( '/^6(?:011|5[0-9]{2})[0-9]{12}$/', $card_num ) ) { // discover
 669+ return 'discover';
 670+ } else { // an unrecognized card type was entered
 671+ return false;
 672+ }
 673+ }
 674+
 675+}
 676+
 677+?>
Property changes on: trunk/extensions/DonationInterface/gateway_common/DataValidator.php
___________________________________________________________________
Added: svn:eol-style
1678 + native

Follow-up revisions

RevisionCommit summaryAuthorDate
r107584followup r107551...khorn18:13, 29 December 2011
r107585followup r107551...khorn18:31, 29 December 2011
r112245New DataValidator class, and donationinterface.php Only: MFT r107299, r10755...khorn21:42, 23 February 2012

Comments

#Comment by Nikerabbit (talk | contribs)   06:55, 29 December 2011
  1. ?>
  2. in wmfMessageExists, can you use wfMessage( ... )->inLanguage()->exists()?
#Comment by Khorn (WMF) (talk | contribs)   17:29, 29 December 2011

Oh, awesome. I was about to start digging around for a better function than wfEmptyMsg. Thank you! Both points will be addressed in the next rev.

#Comment by Khorn (WMF) (talk | contribs)   18:13, 29 December 2011

Done in r107584

Status & tagging log