<?php

/*
 * This code sample contains several functions that work together to enable the efficient import of new (or update of existing) Contacts.
 * Contacts support several standard fields and unlimited custom fields. You'll find that the code in this sample handles these fields.
 * Each Contact is, by default, assigned to an Account (which can be essentially empty or undefined). The code in this sample handles Account standard and custom fields as well.
 */


/*
 * The few lines of code immediately below should be all that are required in order to successfully import Contacts if you are not using any custom fields.
 * If you're using custom fields you'll need to have a look at contactImportSetOne() function (which is pretty well commented).
 *
 * Each Contact you want imported is described by an array. Those contact arrays are in turn nested in a containing array (as in the example immediately below).
 * The functions in this sample will convert all of that into JSON and execute the appropriate API call (ContactImport::setOne)
 *
 * You may pass, potentially, a very large number of Contacts in a single call.
 * The actual number you may pass depends on the number of fields you're including (don't get too crazy with kilobyte size).
 * We can pass 10,000 or more Contacts in call if we're not passing lots of fields.
 *
 * The functions in this file are fairly well commented. Please contact our support team with any questions. We're happy to help :)
 */

$arrContacts = array (
    array (
        
'email' => 'some_email@some_domain.com',
        
'first_name' => 'SomeFirstName',
        
'last_name' => 'SomeLastName'
    
),
    array (
        
'email' => 'another_email@another_domain.com',
        
'first_name' => 'anotherFirstName',
        
'last_name' => 'anotherLastName'
    
)
);

$intImportJobId contactImportSetOne($arrContacts);

function 
contactImportSetOne($arrContacts$arrAddToLists = array(), $arrRemoveFromLists = array()) {
    
/**
     * This is the main function that imports Contacts via API. The other functions in this sample are helpers that perform tasks that make this import easier.
     *
     * You pass to this function an array of Contacts. You may pass many contacts in one call. Depending on the number of fields you are passing for each Contact, you may be able to pass 10,000 Contacts in a single call.
     * Please do not call this method once for each Contact. You must batch your Contacts into a single call whenever possible.
     *
     *
     * This call is asynchronous. It is designed for situations where you may be importing many Contacts simultaneously.
     * It adds your Contacts to a job queue for import and returns to you the id of your job in that queue.
     * Included in this example is a function you can use to check if an import is complete.
     *
     * If you need to import a single Contact synchronously to return a contact_id (for example, when you want to import a single Contact then immediately email that Contact) use the getOrCreateContactIdByContactEmailAddress() method included in this example
     */

    
$intImportJobId null;

    
//build mappings for standard fields (both Contact and Account) automatically...
    
$arrContactStandardColumns buildContactStandardFieldMap($arrContacts[0]); //this will build an array that "maps" the field names passed in your array of Contacts to their actual field names at the Contact level
    
$arrAccountStandardColumns buildAccountStandardFieldMap($arrContacts[0]); //this will build an array that "maps" the field names passed in your array of Contacts to their actual field names at the Account level

    /**
     * Edit the array below to provide a map for custom fields at the Contact level
     */
    
$arrCustomFieldMapContact = array(
        
"the_field_name_in_your_arrContacts_array" => "The Actual Field Name",
        
"intranet_unique_id" => "Intranet Unique ID"//here's an example
        
"tracking_code_installed" => "TrackingCodeInstalled" //here's another example
    
);
    
$arrContactCustomColumns buildCustomFieldMap('Contact'$arrCustomFieldMapContact);

    
/**
     * Edit the array below to provide a map for custom fields at the Account level
     */
    
$arrCustomFieldMapAccount = array(
        
"FreeTrialStartDate" => "FreeTrialStartDate",
        
"FreeTrialExpirationDate" => "FreeTrialExpirationDate"
    
);
    
$arrAccountCustomColumns buildCustomFieldMap('Account'$arrCustomFieldMapAccount);

    
//merge the 4 field map arrays into one...
    
$arrContactImportColumns array_merge($arrContactStandardColumns$arrContactCustomColumns$arrAccountStandardColumns$arrAccountCustomColumns);

    
$arrParams = [
        
'contact_import' => [
            
'contact_import_id' => ''//leave this as an empty string. The API will return a contact_import_id upon success
            
'contact_import_overwrite' => true//true will allow the API to update existing Contacts if you pass in a Contact that already exists. false will cause the API to ignore Contacts in your array if that Contact already exists
            
'ContactImportRecipient' => array('your_email@your_domain.com'), //optional. you may pass an empty array to opt-out of email notifications
            
'ContactImportColumn' => $arrContactImportColumns//the array we assembled previously that maps all standard and custom fields for Contacts and Accounts
            
'ContactImportEmailListAssn' => $arrAddToLists//optional, you may pass an empty array
            
'ContactImportEmailListRemoveAssn' => $arrRemoveFromLists//optional, you may pass an empty array
            
'Contacts' => $arrContacts
        
]
    ];

    
$strResponse executeJsonApiCall('ContactImport''setOne'$arrParams);
    
$objResponse json_decode($strResponse);

    if (
property_exists($objResponse'error') && $objResponse->error !== null) {
        
$strError print_r($objResponsetrue);
        
$strMessage "ERROR: submitContactImport\n\n$strError";
        
//send yourself an email, log to database, throw an exception... handle errors as you like.
    
} else {
        
$intImportJobId $objResponse->result->contact_import_id;
        if (empty(
$_SESSION['strAPIUsername'])) {
            
syslog(LOG_SYSLOG"nr_api_functions.php ContactImport::setOne, import_job_id: $intImportJobId");
        }
        
$strJSONParams json_encode($arrParams);

        
//we recommend logging all import jobs to a database table for auditing purposes. Here is where we insert $strJSONParams to a table
    
}

    return 
$intImportJobId;
}

function 
buildContactStandardFieldMap($arrContact) {
    
/**
     * This "maps" the field names you used in your array of Contacts to their actual field names.
     * This function deals only with "standard Contact fields".
     *
     * Standard fields for Accounts are handled via buildAccountStandardFieldMap().
     * Custom fields for both Contacts and Accounts are handled via buildCustomFieldMap().
     *
     * Look at the $arrStandardFields array below. See that the field called "email_address" from our Contacts array is mapped to the field called "Email Address".
     * You'll also see that this mapping array will handle that mapping whether you pass your email address as "email" or "emailAddress".
     * Modify the left side of this array as needed based on the names of the fields you pass in your array of Contacts
     */

    
$arrContactImportColumn = array();
    
$arrStandardFields = array(
        
'mauuid' => 'Unique Visitor UUID',
        
'contact_id' => 'Unique Contact ID',
        
'company' => 'Company Name',
        
'company_name' => 'Company Name',
        
'email' => 'Email Address',
        
'email_address' => 'Email Address',
        
'emailAddress' => 'Email Address',
        
'first_name' => 'First Name',
        
'firstName' => 'First Name',
        
'last_name' => 'Last Name',
        
'lastName' => 'Last Name',
        
'title' => 'Title',
        
'phone' => 'Work Phone',
        
'fax' => 'Fax',
        
'gdpr_consent_granted' => 'GDPR Consent Granted',
        
'mobile' => 'Mobile Phone',
        
'mobile_phone' => 'Mobile Phone',
        
'mobilePhone' => 'Mobile Phone',
        
'address' => 'Address 1',
        
'address2' => 'Address 2',
        
'city' => 'City',
        
'state' => 'State',
        
'zip' => 'Zip',
        
'country' => 'Country',
        
'lead_owner_email_address' => 'Lead Owner Email Address',
        
'lead_owner_first_name' => 'Lead Owner First Name',
        
'lead_owner_last_name' => 'Lead Owner Last Name',
        
'lead_owner_phone' => 'Lead Owner Phone',
        
'lead_owner_title' => 'Lead Owner Title'
    
);

    foreach (
$arrContact as $strKey => $mixVal) {
        if (
array_key_exists($strKey$arrStandardFields)) {
            
$arrContactImportColumn[] = array(
                
'contact_import_column' => $strKey,
                
'contact_import_attribute' => $arrStandardFields[$strKey],
                
'contact_import_attribute_type_id' => 1
            
);
            continue;
        }
    }

    return 
$arrContactImportColumn;
}

function 
buildAccountStandardFieldMap($arrContact) {
    
/**
     * This "maps" the field names you used in your array of Contacts to their actual field names.
     * This function deals only with "standard Account fields".
     *
     * Standard fields for Contacts are handled via buildContactStandardFieldMap().
     * Custom fields for both Contacts and Accounts are handled via buildCustomFieldMap().
     */

    
$arrContactImportColumn = array();
    
$arrStandardFields = array(
        
"account_id" => "Account ID",
        
"account_name" => "Account Name",
        
"known_isp" => "Known ISP",
        
"account_modified" => "Account Modified",
        
"account_phone" => "Account Phone",
        
"account_fax" => "Account Fax",
        
"account_website" => "Account Website",
        
"account_employees" => "Account Employees",
        
"account_revenue" => "Account Revenue",
        
"account_siccode" => "Account SIC Code",
        
"account_billing_street" => "Account Billing Street",
        
"account_billing_city" => "Account Billing City",
        
"account_billing_state" => "Account Billing State",
        
"account_billing_country" => "Account Billing Country",
        
"account_billing_zip" => "Account Billing Zip",
        
"account_shipping_street" => "Account Shipping Street",
        
"account_shipping_city" => "Account Shipping City",
        
"account_shipping_state" => "Account Shipping State",
        
"account_shipping_country" => "Account Shipping Country",
        
"account_shipping_zip" => "Account Shipping Zip",
        
"account_owner" => "Account Owner",
        
"account_industry" => "Account Industry",
        
"account_description" => "Account Description",
        
"account_type" => "Account Type",
        
"account_status" => "Account Status",
    );

    foreach (
$arrContact as $strKey => $mixVal) {
        if (
array_key_exists($strKey$arrStandardFields)) {
            
$arrContactImportColumn[] = array(
                
'contact_import_column' => $strKey,
                
'contact_import_attribute' => $arrStandardFields[$strKey],
                
'contact_import_attribute_type_id' => 3
            
);
        }
    }

    return 
$arrContactImportColumn;
}

function 
buildCustomFieldMap($strFieldType$arrCustomFieldMapAccount) {
    
/**
     * This function maps custom fields (that may be present in your array of Contacts) to their respective fields.
     * This function handles custom fields for both Contacts and Accounts
     */

    
if ($strFieldType == 'Contact') {
        
$intContactImportAttributeTypeId 2;
    } elseif (
$strFieldType == 'Account') {
        
$intContactImportAttributeTypeId 4;
    } else {
        
$strMessage "buildCustomFieldMap called with invalid strFieldType. The strFieldType passed was $strFieldType";
        
syslog(LOG_SYSLOG$strMessage);
        
//send yourself an email here if you like to notify of a problem.
        
return false;
    }
    
$arrContactImportColumn = array();
    foreach (
$arrCustomFieldMapAccount as $strKey => $strVal) {
        
$arrContactImportColumn[] = array(
            
'contact_import_column' => $strKey,
            
'contact_import_attribute' => $strVal,
            
'contact_import_attribute_type_id' => $intContactImportAttributeTypeId
        
);
    }

    return 
$arrContactImportColumn;
}

function 
executeJsonApiCall($strController$strMethod$arrParams) {

    
syslog(LOG_SYSLOG"nr_api_functions.php execute_json_api_call: $strController::$strMethod");

    
$strRequestId uniqid();

    
$objCurlHandle curl_init('https://apps.net-results.com/api/v2/rpc/server.php?Controller=' $strController);

    
curl_setopt($objCurlHandleCURLOPT_USERPWD"your_username:your_password");
    
curl_setopt($objCurlHandleCURLOPT_SSL_VERIFYHOSTfalse);
    
curl_setopt($objCurlHandleCURLOPT_SSL_VERIFYPEERfalse);
    
curl_setopt($objCurlHandleCURLOPT_RETURNTRANSFERtrue);
    
curl_setopt($objCurlHandleCURLOPT_POSTtrue);
    
curl_setopt($objCurlHandleCURLOPT_POSTFIELDSjson_encode(array('id' => $strRequestId'method' => $strMethod'jsonrpc' => '2.0''params' => $arrParams)));

    
$strResponse curl_exec($objCurlHandle);

    if (empty(
$strResponse)) {
        
$strParams print_r($arrParamstrue);
        
$strErrorMessage "executeJsonApiCall returned an empty strResponse. strController: $strController, strMethod: $strMethod, arrParams: $strParams";
        throw new 
Exception($strErrorMessage);
    }

    if (
$strResponse === false) {
        
syslog(LOG_SYSLOG'CURL ERROR: ' curl_error($objCurlHandle));
        
syslog(LOG_SYSLOG'CONTROLLER: ' $strController ' ' print_r(json_encode(array('id' => $strRequestId'method' => $strMethod'jsonrpc' => '2.0''params' => $arrParams)), true));
    } else {
        
$objResponse json_decode($strResponse);
        if (
property_exists($objResponse'error') && $objResponse->error !== null) {
            
$strParams print_r($arrParamstrue);
            
$strError print_r($objResponse->errortrue);
            
syslog(LOG_SYSLOG"execute_json_api_call ERROR: strRequestId: $strRequestId$strError, controller: $strController, method: $strMethod, params: $strParams");
            
$strTextContent "execute_json_api_call ERROR:\nstrRequestId: $strRequestId\n $strError\n controller: $strController\n method: $strMethod\n params: $strParams";
            
//send yourself an email, log to database, throw an exception... handle errors as you like.
        
}
    }

    return 
$strResponse;
}


function 
checkContactImportCompletionStatusByContactImportId($intContactImportId) {
    
/**
     * This function will check whether your queued job is complete or not and return to you a boolean true or false
     */

    
if (is_numeric($intContactImportId)) {
        
$arrParams = array('contact_import_id' => $intContactImportId);
        
$objResult json_decode(executeJsonApiCall('ContactImport''getStatus'$arrParams));
        if (
is_object($objResult) && $objResult->error === null) {
            if (
$objResult->result->contact_import_completed == 1) {
                return 
true;
            }
        } else {
            return 
false;
        }
    }
    return 
false;
}


function 
getContactIdByContactEmailAddress($strEmail) {
    
/**
     * This function simply executes the Contact::getContactIdByEmailAddress method in the API and returns that contact_id on success or false on failure.
     */

    
$params = array('email_address' => $strEmail);
    
$strResponse executeJsonApiCall('Contact''getContactIdByEmailAddress'$params);
    
$arrResponse json_decode($strResponsetrue);

    if (
array_key_exists('error'$arrResponse) && $arrResponse['error'] != null) {
        
$intContactID false;
    } elseif (empty(
$arrResponse['result']['contact_id'])) {
        
$intContactID false;
    } else {
        
$intContactID $arrResponse['result']['contact_id'];
    }

    return 
$intContactID;
}

function 
getOrCreateContactIdByContactEmailAddress($strEmail) {
    
/**
     * This function allows you to create a single contact "synchronously".
     *
     * We're using the asynchronous ContactImport::setOne method, but we're then checking several times for up to a minute or so to see if the import job queued up by the setOne method is complete.
     * If if is complete we use the getContactIdByContactEmailAddress() function to get our newly created Contact's email address.
     */

    
if (isEmailAddressValid($strEmail) === false) {
        throw new 
Exception("The email address passed ($strEmail) appears to be invalid.");
    }

    
$intContactId getContactIdByContactEmailAddress($strEmail);

    if (
$intContactId === false) {
        
$intImportJobId contactImportSetOne(array(array('email' => $strEmail)));

        if (
$intImportJobId !== false) {
            
$intDelay 100000;
            
$blnIsImportComplete false;

            do {
                
usleep($intDelay);
                
$intDelay *= 10;
                
$objResponse json_decode(executeJsonApiCall('ContactImport''getStatus', array('contact_import_id' => $intImportJobId)));
                
print_r($objResponse);
                if (
is_object($objResponse) && property_exists($objResponse'result') && property_exists($objResponse->result'contact_import_completed') && $objResponse->result->contact_import_completed == 1) {
                    
$blnIsImportComplete true;
                }
            } while (
$blnIsImportComplete === false && $intDelay 10000001);

            
$intContactId getContactIdByContactEmailAddress($strEmail);

        } else {
            throw new 
Exception("getOrCreateContactIdByContactEmailAddress FAILED trying to get a intImportJobId for email address $strEmail");
        }
    }

    if (
$intContactId === false) {
        throw new 
Exception("getOrCreateContactIdByContactEmailAddress FAILED to return a valid contact_id for email address $strEmail");
    }

    return 
$intContactId;
}

function 
isEmailAddressValid($strEmailAddress) {
    
/**
     * This function does a reasonable job of checking that an email address is valid. Returns boolean true or false.
     */

    
$strEmailAddress strtolower(trim($strEmailAddress));

    if (
$strEmailAddress == '' || is_null($strEmailAddress) || !stristr($strEmailAddress'@') || !preg_match('/^.+@(.+\..+)+$/S'$strEmailAddress) || substr_count($strEmailAddress'@') > 1) {
        return 
false;
    }

    if (
mb_strlen($strEmailAddress'UTF-8') > 95) {
        return 
false;
    }

    
$arrBlackList = array(' '';''|''\\''/''?''..''!''=''('')');
    foreach (
$arrBlackList as $strBlackList) {
        if (
strpos($strEmailAddress$strBlackList) !== false) {
            return 
false;
        }
    }

    
//http://data.iana.org/TLD/tlds-alpha-by-domain.txt
    
$arrValidTLDs = array('ABOGADO','AC','ACADEMY','ACCOUNTANTS','ACTIVE','ACTOR','AD','AE','AERO','AF','AG','AGENCY','AI','AIRFORCE','AL','ALLFINANZ','ALSACE','AM','AN','AO','AQ','AR','ARCHI','ARMY','ARPA','AS','ASIA','ASSOCIATES','AT','ATTORNEY','AU','AUCTION','AUDIO','AUTOS','AW','AX','AXA','AZ','BA','BAND','BAR','BARGAINS','BAYERN','BB','BD','BE','BEER','BERLIN','BEST','BF','BG','BH','BI','BID','BIKE','BIO','BIZ','BJ','BLACK','BLACKFRIDAY','BLUE','BM','BMW','BN','BNPPARIBAS','BO','BOO','BOUTIQUE','BR','BRUSSELS','BS','BT','BUDAPEST','BUILD','BUILDERS','BUSINESS','BUZZ','BV','BW','BY','BZ','BZH','CA','CAB','CAL','CAMERA','CAMP','CANCERRESEARCH','CAPETOWN','CAPITAL','CARAVAN','CARDS','CARE','CAREER','CAREERS','CASA','CASH','CAT','CATERING','CC','CD','CENTER','CEO','CERN','CF','CG','CH','CHANNEL','CHEAP','CHRISTMAS','CHROME','CHURCH','CI','CITIC','CITY','CK','CL','CLAIMS','CLEANING','CLICK','CLINIC','CLOTHING','CLUB','CM','CN','CO','CODES','COFFEE','COLLEGE','COLOGNE','COM','COMMUNITY','COMPANY','COMPUTER','CONDOS','CONSTRUCTION','CONSULTING','CONTRACTORS','COOKING','COOL','COOP','COUNTRY','CR','CREDIT','CREDITCARD','CRS','CRUISES','CU','CUISINELLA','CV','CW','CX','CY','CYMRU','CZ','DAD','DANCE','DATING','DAY','DE','DEALS','DEGREE','DEMOCRAT','DENTAL','DENTIST','DESI','DIAMONDS','DIET','DIGITAL','DIRECT','DIRECTORY','DISCOUNT','DJ','DK','DM','DNP','DO','DOMAINS','DURBAN','DVAG','DZ','EAT','EC','EDU','EDUCATION','EE','EG','EMAIL','EMERCK','ENGINEER','ENGINEERING','ENTERPRISES','EQUIPMENT','ER','ES','ESQ','ESTATE','ET','EU','EUS','EVENTS','EXCHANGE','EXPERT','EXPOSED','FAIL','FARM','FEEDBACK','FI','FINANCE','FINANCIAL','FISH','FISHING','FITNESS','FJ','FK','FLIGHTS','FLORIST','FLSMIDTH','FLY','FM','FO','FOO','FORSALE','FOUNDATION','FR','FRL','FROGANS','FUND','FURNITURE','FUTBOL','GA','GAL','GALLERY','GB','GBIZ','GD','GE','GENT','GF','GG','GH','GI','GIFT','GIFTS','GIVES','GL','GLASS','GLE','GLOBAL','GLOBO','GM','GMAIL','GMO','GMX','GN','GOOGLE','GOP','GOV','GP','GQ','GR','GRAPHICS','GRATIS','GREEN','GRIPE','GS','GT','GU','GUIDE','GUITARS','GURU','GW','GY','HAMBURG','HAUS','HEALTHCARE','HELP','HERE','HIPHOP','HIV','HK','HM','HN','HOLDINGS','HOLIDAY','HOMES','HORSE','HOST','HOSTING','HOUSE','HOW','HR','HT','HU','IBM','ID','IE','IL','IM','IMMO','IMMOBILIEN','IN','INDUSTRIES','INFO','ING','INK','INSTITUTE','INSURE','INT','INTERNATIONAL','INVESTMENTS','IO','IQ','IR','IS','IT','JE','JETZT','JM','JO','JOBS','JOBURG','JP','JUEGOS','KAUFEN','KE','KG','KH','KI','KIM','KITCHEN','KIWI','KM','KN','KOELN','KP','KR','KRD','KRED','KW','KY','KZ','LA','LACAIXA','LAND','LAWYER','LB','LC','LEASE','LGBT','LI','LIFE','LIGHTING','LIMITED','LIMO','LINK','LK','LOANS','LONDON','LOTTO','LR','LS','LT','LTDA','LU','LUXE','LUXURY','LV','LY','MA','MAISON','MANAGEMENT','MANGO','MARKET','MARKETING','MC','MD','ME','MEDIA','MEET','MELBOURNE','MEME','MENU','MG','MH','MIAMI','MIL','MINI','MK','ML','MM','MN','MO','MOBI','MODA','MOE','MONASH','MORTGAGE','MOSCOW','MOTORCYCLES','MOV','MP','MQ','MR','MS','MT','MU','MUSEUM','MV','MW','MX','MY','MZ','NA','NAGOYA','NAME','NAVY','NC','NE','NET','NETWORK','NEUSTAR','NEW','NEXUS','NF','NG','NGO','NHK','NI','NINJA','NL','NO','NP','NR','NRA','NRW','NU','NYC','NZ','OKINAWA','OM','ONG','ONL','OOO','ORG','ORGANIC','OTSUKA','OVH','PA','PARIS','PARTNERS','PARTS','PE','PF','PG','PH','PHARMACY','PHOTO','PHOTOGRAPHY','PHOTOS','PHYSIO','PICS','PICTURES','PINK','PIZZA','PK','PL','PLACE','PLUMBING','PM','PN','POHL','POKER','POST','PR','PRAXI','PRESS','PRO','PROD','PRODUCTIONS','PROF','PROPERTIES','PROPERTY','PS','PT','PUB','PW','PY','QA','QPON','QUEBEC','RE','REALTOR','RECIPES','RED','REHAB','REISE','REISEN','REN','RENTALS','REPAIR','REPORT','REPUBLICAN','REST','RESTAURANT','REVIEWS','RICH','RIO','RIP','RO','ROCKS','RODEO','RS','RSVP','RU','RUHR','RW','RYUKYU','SA','SAARLAND','SARL','SB','SC','SCA','SCB','SCHMIDT','SCHULE','SCOT','SD','SE','SERVICES','SEXY','SG','SH','SHIKSHA','SHOES','SI','SINGLES','SJ','SK','SL','SM','SN','SO','SOCIAL','SOFTWARE','SOHU','SOLAR','SOLUTIONS','SOY','SPACE','SPIEGEL','SR','ST','SU','SUPPLIES','SUPPLY','SUPPORT','SURF','SURGERY','SUZUKI','SV','SX','SY','SYSTEMS','SZ','TAIPEI','TATAR','TATTOO','TAX','TC','TD','TECHNOLOGY','TEL','TF','TG','TH','TIENDA','TIPS','TIROL','TJ','TK','TL','TM','TN','TO','TODAY','TOKYO','TOOLS','TOP','TOWN','TOYS','TP','TR','TRADE','TRAINING','TRAVEL','TT','TUI','TV','TW','TZ','UA','UG','UK','UNIVERSITY','UNO','UOL','US','UY','UZ','VA','VACATIONS','VC','VE','VEGAS','VENTURES','VERSICHERUNG','VET','VG','VI','VIAJES','VILLAS','VISION','VLAANDEREN','VN','VODKA','VOTE','VOTING','VOTO','VOYAGE','VU','WALES','WANG','WATCH','WEBCAM','WEBSITE','WED','WEDDING','WF','WHOSWHO','WIEN','WIKI','WILLIAMHILL','WME','WORK','WORKS','WORLD','WS','WTC','WTF','XXX','XYZ','YACHTS','YANDEX','YE','YOGA','YOKOHAMA','YOUTUBE','YT','ZA','ZIP','ZM','ZONE','ZW');

    
//match the very last .()
    
$arrMatches = array();
    if (
preg_match('/.*@.*\.(.*)/S'$strEmailAddress$arrMatches) && !preg_match('/.*@.*_.*/S'$strEmailAddress) && !preg_match('/.*\.@.*/S'$strEmailAddress) && !preg_match('/.*@\..*/S'$strEmailAddress)) {
        if (
count($arrMatches) > 0) {
            
$tld array_pop($arrMatches);
            if (
in_array(strtoupper($tld), $arrValidTLDs)) {
                return 
true;
            } else {
                return 
false;
            }
        } else {
            return 
false;
        }
    } else {
        return 
false;
    }
}