Gmail contact import class fails when requesting more than 25 contacts in Drupal

I’m using a module in Drupal that imports contacts from Gmail. The script works fine when it retrieves the default 25 contacts. However, when I try to fetch more contacts by modifying the Google API URL with a max-results parameter, it results in an error.

Issue: When I change the API feed URL by adding ?max-results=1000, I encounter a fatal error:

Fatal error: Call to a member function getAttribute() on a non-object

This error occurs at the line where the code attempts to acquire the email address: $emailAddr = $emailNode->getAttribute('address');

class ContactImporter {
    static $login_url = 'https://www.google.com/accounts/ClientLogin';
    static $contacts_feed = 'http://www.google.com/m8/feeds/contacts/default/thin?max-results=65535';

    function createCurlConnection($endpoint, $authToken = null) {
        $ch = curl_init();
        
        $options = array(
            CURLOPT_URL => $endpoint,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_SSL_VERIFYPEER => false,
        );
        
        if ($authToken != null) {
            $options[CURLOPT_HTTPHEADER] = array(
               'Authorization: GoogleLogin auth='.$authToken,
            );
        }
        curl_setopt_array($ch, $options);
        return $ch;
    }

    function setupPostRequest($curlHandle, $postData) {
        curl_setopt($curlHandle, CURLOPT_POST, true);
        curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $postData);
        return $curlHandle;
    }

    function authenticate($username, $pass) {
        $ch = $this->setupPostRequest($this->createCurlConnection(self::$login_url), array(
           'accountType' => 'HOSTED_OR_GOOGLE',
           'Email' => $username,
           'Passwd' => $pass,
           'service' => 'cp',
           'source' => 'ContactImporter',
        ));
        $response = curl_exec($ch);

        preg_match_all('/Auth=([^\s]*)/si', $response, $authMatches);
        if (isset($authMatches[1][0])) {
            return $authMatches[1][0];
        }
        return false;
    }

    function fetchContacts($username, $pass) {
        $authToken = $this->authenticate($username, $pass);
        if (!$authToken) {
            return false;
        }

        $ch = $this->createCurlConnection(self::$contacts_feed, $authToken);
        $xmlData = curl_exec($ch);

        $document = new DOMDocument();
        $document->loadXML($xmlData);
        $xpath = new DOMXPath($document);
        $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom');
        $xpath->registerNamespace('gdata', 'http://schemas.google.com/g/2005');
        $entryList = $xpath->query('//atom:entry');
        $totalEntries = $entryList->length;

        $contactList = array();
        for ($i = 0; $i < $totalEntries; $i++) {
            $currentEntry = $entryList->item($i);
            $titleNodes = $xpath->query('atom:title', $currentEntry);
            $nameNode = $titleNodes->item(0);
            $contactName = $nameNode->textContent;
            $emailNodes = $xpath->query('gdata:email', $currentEntry);
            $emailNode = $emailNodes->item(0);
            $emailAddr = $emailNode->getAttribute('address');
            
            if (empty($contactName)) {
                $contactList[] = $emailAddr;
            } else {
                $contactList[$contactName] = $emailAddr;
            }
        }
        
        return $contactList;
    }
}

The error happens on the getAttribute('address') line. It appears that $emailNode is null when dealing with larger sets of results. How can I resolve this issue to retrieve all contacts instead of just 25?

some contacts dont have email addrs in big batches. check if $emailNode exists b4 calling getAttribute - just add an if ($emailNode) check b4 the getAttribute line or u’ll keep getting null obj errors.

The issue arises when fetching larger contact sets, as some entries may not contain email addresses. When your XPath query finds no email elements, it returns null, leading to a fatal error on the getAttribute call. I experienced this problem while working with the Google Contacts API myself.

To avoid this, check if the email node exists before processing it. Modify your loop as follows:

$emailNodes = $xpath->query('gdata:email', $currentEntry);
if ($emailNodes->length > 0) {
    $emailNode = $emailNodes->item(0);
    $emailAddr = $emailNode->getAttribute('address');
    // handle contact
} else {
    // skip or handle contact without email
    continue;
}

Also, consider switching to the full feed format for more reliable data.

This happens because Google Contacts API returns entries without email addresses in larger result sets. I hit the same issue building a contact sync feature - your code assumes every contact has an email, but many contacts only have phone numbers or other data. Don’t just check for null values. You also need to validate your XPath namespace registration. The XML structure sometimes changes between small and large result sets. I’d log the raw XML response first to see what you’re actually getting. You might find some entries use different namespaces or have nested email structures your current XPath misses. Also, the thin feed format might not return complete data for all contacts in large batches. The full feed gives more consistent results but needs slightly different parsing logic.