Integrate your PHP application with Google Contacts

Read and write contact information from Google Contacts with XML and PHP

The Google Contacts Data API provides a powerful, client-neutral API to read and modify a user's private Gmail contact information. Learn to retrieve, add, delete, and modify contacts through a custom PHP application with this API in an application context.

Share:

Vikram Vaswani, Founder, Melonfire

Photo of Vikram VaswaniVikram Vaswani is the founder and CEO of Melonfire, a consulting services firm with special expertise in open-source tools and technologies. He is also the author of the books PHP Programming Solutions and PHP: A Beginners Guide.



22 September 2009

Also available in Russian Japanese Vietnamese Portuguese

Introduction

Since much of my communication with friends and colleagues takes place over e-mail, I've become used to storing contact information in the address book application that ships with Mozilla Thunderbird, my default e-mail client. However, for the last couple of months, I've gradually moved information over to Gmail, which comes with a powerful address book of its own. This feature, known as Google Contacts, is easily accessible over the Web, supports a large number of information fields and has both import and export features. This makes it possible to not only store comprehensive data on contacts, but to have that contact information "travel" with you from location to location, because you can access it from any Web-accessible computer.

Frequently used acronyms

  • API: Application program interface
  • DOM: Document Object Model
  • HTTP: Hypertext Transfer Protocol
  • REST: Representational State Transfer
  • URL: Uniform Resource Locator
  • XML: Extensible Markup Language

From a development perspective too, Google Contacts is interesting: Like many other Google products, it exposes a Data API which allows developers to easily build new applications around the data stored in a private address book. This API, which follows the REST model, can be accessed through any XML-capable development toolkit, and already has client libraries for many common programming languages...including my favourite, PHP.

This article will introduce you to the Google Contacts Data API, and show you how to integrate and use contact data with a custom PHP application. In the examples, you'll retrieve contacts from a user's address book, add new contacts, modify and delete contacts, and control the data returned in a contact feed. So come on in, and let's get started!


Understanding the Google Contacts API

Before you dive into the PHP code, a few words about the Google Contacts Data API are in order. As with other Google Data APIs, the system accepts HTTP requests that contain one or more XML-encoded input arguments and return Atom feeds that contain the requested information. These feeds can then be parsed in any XML-aware programming language. In addition, the standard HTTP operations of GET, POST, PUT, and DELETE are mapped to API methods that retrieve, add, update, and delete records respectively.

A typical Google Contacts feed contains a lot of information. Consider the example in Listing 1:

Listing 1: An example Google Contacts feed
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom">
  <atom:author>
    <atom:name>John Doe</atom:name>
    <atom:email>user@gmail.com</atom:email>
  </atom:author>
  <atom:category term="http://schemas.google.com/contact/2008#contact" 
   scheme="http://schemas.google.com/g/2005#kind"/>
  <atom:id>user@gmail.com</atom:id>
  <atom:link href="http://www.google.com/" rel="alternate" type="text/html"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full" 
   rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full" 
   rel="http://schemas.google.com/g/2005#post" type="application/atom+xml"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full/
   batch" rel="http://schemas.google.com/g/2005#batch" type="application/atom+xml"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full?
   max-results=25" rel="self" type="application/atom+xml"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full?
   start-index=26&max-results=25" rel="next" type="application/atom+xml"/>
  <atom:title type="text">John Doe's Contacts</atom:title>
  <atom:updated>2009-08-31T10:48:00.410Z</atom:updated>
  <atom:generator uri="http://www.google.com/m8/feeds" version="1.0">Contacts
  </atom:generator>
  <atom:entry xmlns:default="http://www.w3.org/2007/app" 
   xmlns:default1="http://schemas.google.com/g/2005" 
   xmlns:default2="http://schemas.google.com/contact/2008">
    <default:edited xmlns="http://www.w3.org/2007/app">2009-08-22T16:52:37.457Z
    </default:edited>
    <default1:name xmlns="http://schemas.google.com/g/2005">
      <default1:fullName>Vikram Vaswani</default1:fullName>
      <default1:givenName>Vikram</default1:givenName>
      <default1:familyName>Vaswani</default1:familyName>
    </default1:name>
    <default1:organization xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#work">
      <default1:orgName>Melonfire</default1:orgName>
      <default1:orgTitle>CEO</default1:orgTitle>
    </default1:organization>
    <default1:email xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#other" address="vikram@example.org" 
     primary="true"/>
    <default1:email xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#home" address="vikram@example.com"/>
    <default1:phoneNumber xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#mobile">0012345678901
     </default1:phoneNumber>
    <default1:phoneNumber xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#work_fax">0045678901234
     </default1:phoneNumber>
    <default2:website xmlns="http://schemas.google.com/contact/2008" 
     href="http://www.melonfire.com/" rel="home"/>
    <default2:website xmlns="http://schemas.google.com/contact/2008" 
     href="http://www.php-beginners-guide.com/" rel="blog"/>
    <default2:groupMembershipInfo xmlns="http://schemas.google.com/contact/2008" 
     deleted="false" 
     href="http://www.google.com/m8/feeds/groups/user%40gmail.com/base/6"/>
    <atom:category term="http://schemas.google.com/contact/2008#contact" 
     scheme="http://schemas.google.com/g/2005#kind"/>
    <atom:id>http://www.google.com/m8/feeds/contacts/user%40gmail.com/base/0
    </atom:id>
    <atom:link href="http://www.google.com/m8/feeds/photos/media/user%40gmail.com/0" 
     rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*"/>
    <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full/0" 
     rel="self" type="application/atom+xml"/>
    <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full/0" 
     rel="edit" type="application/atom+xml"/>
    <atom:title type="text">Vikram Vaswani</atom:title>
    <atom:updated>2009-08-22T16:52:37.457Z</atom:updated>
    <atom:content type="text">PHP enthusiast</atom:content>
  </atom:entry>
  <atom:entry xmlns:default="http://www.w3.org/2007/app" 
   xmlns:default1="http://schemas.google.com/g/2005">
  </atom:entry>
  <openSearch:totalResults xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"
  >153</openSearch:totalResults>
  <openSearch:startIndex xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"
  >1</openSearch:startIndex>
  <openSearch:itemsPerPage xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"
  >25</openSearch:itemsPerPage>
</atom:feed>

Every Contacts feed opens with a <feed> element as the root element. The <feed> element contains <link> elements, which contain URLs for different versions of the feed, and <openSearch:> elements, which contain summary statistics.

The outermost <feed> element encloses one or more <entry> elements, each representing a single contact. Each <entry> contains further detail, including the contact's name, organization, title, e-mail addresses, phone and fax numbers, Web sites and photograph (among other things). Each <entry> also contains two further <link> elements, <link rel="self" ...> and <link rel="edit" ...>, which hold the URLs for retrieving the complete entry and for editing the entry, respectively.


Retrieving contacts

It's important to note that Google Contacts feeds are private. This means that any operation on the data contained in the feed—including merely viewing it— will only be successful if the operation is authenticated with the username and password of the feed owner using one of the two Google-approved authentication methods: AuthSub or ClientLogin.

To perform this type of authentication manually is a fairly messy task, and requires a fair bit of code to account for the various scenarios that might crop up during a typical authentication transaction. Fortunately, you don't have to worry too much about this: Zend's® GData Client Library, designed specifically for developers who integrate PHP applications with the Google Data API, handles all the details for you. This library, which you can download separately (see Resources for a link), provides a convenient, object-oriented interface to the Google Data API, encapsulate a most common tasks (including authentication), and leaves you free to focus on the core functions of your application. The remaining examples in this article will make use of this library, so ensure you have it installed before proceeding.

Once you install Zend's GData library, let's proceed to an example of processing a Google Contacts Data API feed using PHP. Listing 2 takes the feed from Listing 1 and uses SimpleXML to extract relevant fragments of data from it and format it into a Web page:

Listing 2: Retrieving and displaying contacts
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing contacts</title>
    <style>
    body {
      font-family: Verdana;      
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }
    div.entry {
      display: inline;
      float: left;
      width: 400px;
      height: 150px;
      border: 2px solid; 
      margin: 10px;
      padding: 5px;
    }
    td {
      vertical-align: top;
    }
    </style>    
  </head>
  <body>
     
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Http_Client');
    Zend_Loader::loadClass('Zend_Gdata_Query');
    Zend_Loader::loadClass('Zend_Gdata_Feed');
    
    // set credentials for ClientLogin authentication
    $user = "user@gmail.com";
    $pass = "guessme";
    
    try {
      // perform login and set protocol version to 3.0
      $client = Zend_Gdata_ClientLogin::getHttpClient(
        $user, $pass, 'cp');
      $gdata = new Zend_Gdata($client);
      $gdata->setMajorProtocolVersion(3);
      
      // perform query and get result feed
      $query = new Zend_Gdata_Query(
        'http://www.google.com/m8/feeds/contacts/default/full');
      $feed = $gdata->getFeed($query);
      
      // display title and result count
      ?>
      
      <h2><?php echo $feed->title; ?></h2>
      <div>
      <?php echo $feed->totalResults; ?> contact(s) found.
      </div>
      
      <?php
      // parse feed and extract contact information
      // into simpler objects
      $results = array();
      foreach($feed as $entry){
        $xml = simplexml_load_string($entry->getXML());
        $obj = new stdClass;
        $obj->name = (string) $entry->title;
        $obj->orgName = (string) $xml->organization->orgName; 
        $obj->orgTitle = (string) $xml->organization->orgTitle; 
      
        foreach ($xml->email as $e) {
          $obj->emailAddress[] = (string) $e['address'];
        }
        
        foreach ($xml->phoneNumber as $p) {
          $obj->phoneNumber[] = (string) $p;
        }
        foreach ($xml->website as $w) {
          $obj->website[] = (string) $w['href'];
        }
        
        $results[] = $obj;  
      }
    } catch (Exception $e) {
      die('ERROR:' . $e->getMessage());  
    }
    ?>
    
    <?php
    // display results
    foreach ($results as $r) {
    ?>
    <div class="entry">
      <div class="name"><?php echo (!empty($r->name)) ? 
       $r->name : 'Name not available'; ?></div>
      <div class="data">
        <table>
          <tr>
            <td>Organization</td>
            <td><?php echo $r->orgName; ?></td>
          </tr>
          <tr>
            <td>Email</td>
            <td><?php echo @join(', ', $r->emailAddress); ?></td>
          </tr>
          <tr>
            <td>Phone</td>
            <td><?php echo @join(', ', $r->phoneNumber); ?></td>
          </tr>
          <tr>
            <td>Web</td>
            <td><?php echo @join(', ', $r->website); ?></td>
          </tr>
        </table>
      </div>
    </div>
    <?php
    }
    ?>

  </body>
</html>

Figure 1 demonstrates the output you might see. In the screen capture, some personal e-mail address information is masked.

Figure 1. A Web page that lists contacts
Screen capture of a Web page that lists contacts

First Listing 2 loads the Zend class libraries, and then initializes an instance of the Zend_Gdata service class. This class makes use of a Zend_Http_Client object, which is provided with the necessary user authentication information and used to open an authenticated connection to the Google Contacts service. Once an authenticated connection is open, the getFeed() method retrieves the contacts feed. This method accepts a Zend_Gdata_Query object, which is passed the feed URL. Note also the call to the setMajorProtocolVersion() in Listing 2, which tells the server to use v3.0 of the Google Contacts Data API; this is a required call. If you omit it, you might see unexpected behaviour.

The response to the getFeed() API call is an XML feed similar to that in Listing 1, which is then parsed and converted into a PHP object. Entries within the feed are represented as array elements, making it possible to iterate over the feed and retrieve the individual contact entries. Since SimpleXML provides an easier way to retrieve specific pieces of information from an XML tree, the simplexml_load_string() method is used to convert the XML of each entry into a SimpleXML object. Child nodes under each <entry> are then retrieved and flattened into a simpler object for easy retrieval, and each resulting object is then placed in the $results array. Once the entire feed is processed, you can easily iterate over the $results array with a foreach() loop and format the contents for display in a Web browser.

A few words here about the feed URL passed to the Zend_Gdata_Query object: it's generated by appending the user name and feed type to the standard feed URL, in the http://www.google.com/m8/feeds/contacts/USERID/TYPE. Feed types come in two varieties, full and thin, with the latter returning only a subset of the entire entry. And using the special keyword default in place of the user name, as in Listing 2, returns the user's default feed.


Adding contacts

So that takes care of listing contacts. Now, how about adding new contacts?

This is actually not as complicated as it might seem. To add a new contact, simply POST a new XML-encoded <entry> block to the feed URL. Listing 3 has an example of one such block:

Listing 3: An example Google Contacts entry
<?xml version="1.0"?>
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" 
 xmlns:gd="http://schemas.google.com/g/2005">
  <gd:name>
    <gd:fullName>Jack Frost</gd:fullName>
  </gd:name>
  <gd:email address="jack.frost@example.com" 
   rel="http://schemas.google.com/g/2005#home"/>
  <gd:organization rel="http://schemas.google.com/g/2005#work">
    <gd:orgName>Winter Inc.</gd:orgName>
  </gd:organization>
</atom:entry>

If you use the Zend library, things are even simpler: all you need to do is call the insertEntry() method, which creates a POST request and transmitting the data to the feed URL. Listing 4 has an example:

Listing 4: Adding a new contact
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "user@gmail.com";
$pass = "guessme";

try {
  // perform login and set protocol version to 3.0
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'cp');
  $gdata = new Zend_Gdata($client);
  $gdata->setMajorProtocolVersion(3);
  
  // create new entry
  $doc  = new DOMDocument();
  $doc->formatOutput = true;
  $entry = $doc->createElement('atom:entry');
  $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
   'xmlns:atom', 'http://www.w3.org/2005/Atom');
  $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
   'xmlns:gd', 'http://schemas.google.com/g/2005');
  $doc->appendChild($entry);
  
  // add name element
  $name = $doc->createElement('gd:name');
  $entry->appendChild($name);
  $fullName = $doc->createElement('gd:fullName', 'Jack Frost');
  $name->appendChild($fullName);
  
  // add email element
  $email = $doc->createElement('gd:email');
  $email->setAttribute('address' ,'jack.frost@example.com');
  $email->setAttribute('rel' ,'http://schemas.google.com/g/2005#home');
  $entry->appendChild($email);
  
  // add org name element
  $org = $doc->createElement('gd:organization');
  $org->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
  $entry->appendChild($org);
  $orgName = $doc->createElement('gd:orgName', 'Winter Inc.');
  $org->appendChild($orgName);
  
  // insert entry
  $entryResult = $gdata->insertEntry($doc->saveXML(), 
   'http://www.google.com/m8/feeds/contacts/default/full');
  echo '<h2>Add Contact</h2>';
  echo 'The ID of the new entry is: ' . $entryResult->id;
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());
}
?>

Listing 4 opens an authenticated connection to the Google Contacts Data API using the Zend_Gdata object, as seen earlier in Listing 2. Next, the DOM extension in PHP dynamically constructs the XML <entry> shown in Listing 3, and the insertEntry() method is used to actually save the resulting XML to the Google servers. Once the entry is added, it should immediately become visible in the Google Contacts interface.

Note that you can explicitly mark e-mail addresses and phone numbers as belonging to "home", "work" or "mobile" by specifying the appropriate schema in the <gd:organization rel="..." /> element.

Figure 2 illustrates the output after successfully adding a new contact entry:

Figure 2. The output after you add a new contact
Screen capture of the output after you add a new contact

In the screen capture in Figure 2, some fields are masked in the confirmation message from Google. The message is in the format: The ID of the new entry is http://www.google.com/m8/feeds/contacts/USER_ID/base/CONTACT_ID.

And Figure 3 shows how it looks in the Google Contacts interface:

Figure 3. The newly-added contact in Gmail
Screen capture of the newly-added contact, Jack Frost, in Gmail

Deleting and updating contacts

The Google Contacts Data API also permits you to edit and delete entries. To delete an entry, send a DELETE request to the edit URL of the contact, which is the URL specified in the entry's <link rel="edit" ...> element. In the Zend library context, you can simply pass this URL to the delete() method of the Zend_Gdata object, as illustrated in Listing 5:

Listing 5: Deleting a contact
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "user@gmail.com";
$pass = "guessme";

// set ID of entry to delete
// from <link rel=edit>
$id = 'http://www.google.com/m8/feeds/contacts/default/base/29e98jf648c495c7b';

try {
  // perform login and set protocol version to 3.0
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'cp');
  $client->setHeaders('If-Match: *');
  $gdata = new Zend_Gdata($client);
  $gdata->setMajorProtocolVersion(3);
  
  // delete entry
  $gdata->delete($id);
  echo '<h2>Delete Contact</h2>';
  echo 'Entry deleted';
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());
}
?>

To edit an entry, use the getEntry() method to retrieve the entry using its unique identifier, change the values you wish to update. Then save the entry back to the server with the updateEntry() method of the Zend_Gdata object, which sends a PUT request to the URL specified in the <link rel="self" ...> element of the entry. Listing 6 illustrates the process, setting a new name and e-mail address for a contact entry:

Listing 6: Modifying a contact
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "user@gmail.com";
$pass = "guessme";

// set ID of entry to update
// from <link rel=self>
$id = 'http://www.google.com/m8/feeds/contacts/default/full/0';

try {
  // perform login and set protocol version to 3.0
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'cp');
  $client->setHeaders('If-Match: *');

  $gdata = new Zend_Gdata($client);
  $gdata->setMajorProtocolVersion(3);
  
  // perform query and get entry
  $query = new Zend_Gdata_Query($id);
  $entry = $gdata->getEntry($query);
  $xml = simplexml_load_string($entry->getXML());

  // change name
  $xml->name->fullName = 'John Rabbit';
  
  // change primary email address  
  foreach ($xml->email as $email) {
    if (isset($email['primary'])) {
      $email['address'] = 'jr@example.com';  
    }  
  }
  
  // update entry
  $entryResult = $gdata->updateEntry($xml->saveXML(), 
   $entry->getEditLink()->href);
  echo 'Entry updated';
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());
}
?>

Notice that Listing 5 and Listing 6 both add an additional "If-Match" header as part of the HTTP request. This header is of particular benefit in a multi-client environment, as it can be used to delete or update an entry only if it hasn't already been changed since it was last read. The Google Contacts Data API Developers Guide (see Resources for a link) discusses this header in more detail.


Using additional parameters

The Google Contacts Data API also supports a number of additional parameters that you can use to control the result feed. Here's a list:

  • The start-index parameter specifies the start offset for the feed
  • The max-results parameter specifies the number of entries in the feed
  • The orderby and sortorder parameters specify how to sort feed entries
  • The showdeleted parameter includes entries deleted in the last 30 days in the feed

If you use the Zend client library, you can set many of these parameters simply by setting the corresponding property of the Zend_Gdata_Query object. For those parameters that are specific to the service, the Zend_Gdata_Query object also offers a setParam() method. Both these techniques are illustrated in Listing 7, which restricts the result set to 10 entries, sorted in descending order by their last modified date.

Listing 7: Setting additional query parameters
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "user@gmail.com";
$pass = "guessme";

try {
  // perform login and set protocol version to 3.0
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'cp');
  $gdata = new Zend_Gdata($client);
  $gdata->setMajorProtocolVersion(3);
  
  // perform query and get result feed
  $query = new Zend_Gdata_Query(
    'http://www.google.com/m8/feeds/contacts/default/full');
  $query->maxResults = 10;
  $query->setParam('orderby', 'lastmodified');
  $query->setParam('sortorder', 'descending');
  $feed = $gdata->getFeed($query);

  // display title and result count
  // snip...
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>

A simple application

Now that you know how the Google Contacts Data API works, you can try to build a simple PHP application that allows contact entry and manipulation. This prototype application has 3 scripts (see Download for a zipped file):

  • contacts-index.php lists contact entries and provides links to add and delete entries.
  • contacts-save.php displays a form to add a new entry, and processes the submission.
  • contacts-delete.php deletes a specified entry.

Begin with the first script and update Listing 1 with some additional links, as in Listing 8:

Listing 8: Retrieving and displaying contacts
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing contacts</title>
    <style>
    body {
      font-family: Verdana;      
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }
    div.entry {
      display: inline;
      float: left;
      width: 450px;
      height: 150px;
      border: 2px solid; 
      margin: 10px;
      padding: 5px;
    }
    td {
      vertical-align: top;
    }
    span.links {
      float: right;  
    }
    </style>    
  </head>
  <body>
    <h2>Contacts</h2>
    
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Http_Client');
    Zend_Loader::loadClass('Zend_Gdata_Query');
    Zend_Loader::loadClass('Zend_Gdata_Feed');
    
    // set credentials for ClientLogin authentication
    $user = "user@gmail.com";
    $pass = "guessme";
    
    try {
      // perform login and set protocol version to 3.0
      $client = Zend_Gdata_ClientLogin::getHttpClient(
        $user, $pass, 'cp');
      $gdata = new Zend_Gdata($client);
      $gdata->setMajorProtocolVersion(3);
      
      // perform query and get feed of all results
      $query = new Zend_Gdata_Query(
        'http://www.google.com/m8/feeds/contacts/default/full');
      $query->maxResults = 0;
      $query->setParam('orderby', 'lastmodified');
      $query->setParam('sortorder', 'descending');
      $feed = $gdata->getFeed($query);
      
      // display number of results
      ?>
      
      <h2><?php echo $feed->title; ?></h2>
      <div>
      <?php echo $feed->totalResults; ?> contact(s) found.
      </div>
            
      <?php
      // parse feed and extract contact information
      // into simpler objects
      $results = array();
      foreach($feed as $entry){
        $obj = new stdClass;
        $obj->edit = $entry->getEditLink()->href;
        $xml = simplexml_load_string($entry->getXML());
        $obj->name = (string) $entry->title;
        $obj->orgName = (string) $xml->organization->orgName; 
        $obj->orgTitle = (string) $xml->organization->orgTitle; 
      
        foreach ($xml->email as $e) {
          $obj->emailAddress[] = (string) $e['address'];
        }
        
        foreach ($xml->phoneNumber as $p) {
          $obj->phoneNumber[] = (string) $p;
        }
        foreach ($xml->website as $w) {
          $obj->website[] = (string) $w['href'];
        }
        
        $results[] = $obj;  
      }
    } catch (Exception $e) {
      die('ERROR:' . $e->getMessage());  
    }
    ?>
    
    <div>
    <a href="contacts-save.php">Add a new contact</a>
    </div>    
    
    <?php    
    // display results
    foreach ($results as $r) {
    ?>
    <div class="entry">
      <div class="name"><?php echo (!empty($r->name)) 
       ? $r->name : 'Name not available'; ?> 
        <span class="links"><a href="contacts-delete.php?
         id=<?php echo $r->edit; ?>">Delete</a></span>
      </div>
      <div class="data">
        <table>
          <tr>
            <td>Organization:</td>
            <td><?php echo $r->orgName; ?></td>
          </tr>
          <tr>
            <td>Email:</td>
            <td><?php echo @join(', ', $r->emailAddress); ?></td>
          </tr>
          <tr>
            <td>Phone:</td>
            <td><?php echo @join(', ', $r->phoneNumber); ?></td>
          </tr>
          <tr>
            <td>Web:</td>
            <td><?php echo @join(', ', $r->website); ?></td>
          </tr>
        </table>
      </div>
    </div>
    <?php
    }
    ?>

  </body>
</html>

Figure 4 illustrates the revised output.

Figure 4. A Web page that lists contacts, with add and delete functions
Screen capture of a Web page that lists contacts, with add and delete functions

You'll see, from Figure 4, that there's now an additional Delete link next to each entry. This link includes the entry ID, which is passed to the link target as a GET parameter. The target script reads this ID and deletes it using the technique outlined in Listing 5. Listing 9 shows what the code looks like:

Listing 9: Deleting contacts
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Deleting contacts</title>
  </head>
  <body>
    <h2>Delete Contact</h2>    

    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Http_Client');
    Zend_Loader::loadClass('Zend_Gdata_Query');
    Zend_Loader::loadClass('Zend_Gdata_Feed');
    
    // set credentials for ClientLogin authentication
    $user = "user@gmail.com";
    $pass = "guessme";
    
    if (empty($_GET['id'])) {
      die('ERROR: Missing ID');
    } 

    try {
      // perform login and set protocol version to 3.0
      $client = Zend_Gdata_ClientLogin::getHttpClient(
        $user, $pass, 'cp');
      // set GData delete headers
      $client->setHeaders('If-Match: *');
      $gdata = new Zend_Gdata($client);
      $gdata->setMajorProtocolVersion(3);
      
      // delete entry
      $gdata->delete($_GET['id']);
      echo 'Entry deleted';
    } catch (Exception $e) {
      die('ERROR:' . $e->getMessage());
    }
    ?>
  </body>
</html>

That takes care of listing and removing entries. But what about adding them? Listing 10 introduces another script that allows users to add new contacts through a Web form. It then uses the Zend library to construct the corresponding POST packet and save the data to the server:

Listing 10: Adding new contacts
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Adding new contacts</title>
    <style>
    body {
      font-family: Verdana;      
    }
    </style>
  </head>
  <body>
    <h2>Add Contact</h2>    
    
    <?php if (!isset($_POST['submit'])) { ?>    
    <form method="post" 
     action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>">
      Name: <br/>
      <input name="name" type="text" size="15" /><p/>
      Email address(es): <br/>
      <textarea name="email" type="text"></textarea><p/>
      Organization: <br/>
      <input name="org" type="text" size="15" /><p/>
      <input name="submit" type="submit" value="Save" />      
    </form>    
    
    <?php 
    } else { 

      // check for required input      
      if (empty($_POST['name'])) {
        die('ERROR: Missing name');
      } 
      
      if (empty($_POST['email'])) {
        die('ERROR: Missing email address');
      } 
      
      if (empty($_POST['org'])) {
        die('ERROR: Missing organization');
      } 
      
      // sanitize input and save to array
      $inputData['name'] = htmlentities($_POST['name']);
      $inputData['email'] = htmlentities($_POST['email']);
      $inputData['org'] = htmlentities($_POST['org']);
      
      // load Zend Gdata libraries
      require_once 'Zend/Loader.php';
      Zend_Loader::loadClass('Zend_Gdata');
      Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
      Zend_Loader::loadClass('Zend_Http_Client');
      Zend_Loader::loadClass('Zend_Gdata_Query');
      Zend_Loader::loadClass('Zend_Gdata_Feed');
      
      // set credentials for ClientLogin authentication
      $user = "user@gmail.com";
      $pass = "guessme";
      
      try {
        // perform login and set protocol version to 3.0
        $client = Zend_Gdata_ClientLogin::getHttpClient(
          $user, $pass, 'cp');
        $gdata = new Zend_Gdata($client);
        $gdata->setMajorProtocolVersion(3);
        
        // create new entry
        $doc  = new DOMDocument();
        $doc->formatOutput = true;
        $entry = $doc->createElement('atom:entry');
        $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
         'xmlns:atom', 'http://www.w3.org/2005/Atom');
        $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
         'xmlns:gd', 'http://schemas.google.com/g/2005');
        $doc->appendChild($entry);
        
        // add name element
        $name = $doc->createElement('gd:name');
        $entry->appendChild($name);
        $fullName = $doc->createElement('gd:fullName', $inputData['name']);
        $name->appendChild($fullName);
        
        // add email elements
        $arr = explode(',', $inputData['email']);
        foreach ($arr as $a) {
          $email = $doc->createElement('gd:email'); 
          $email->setAttribute('address', $a);
          $email->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
          $entry->appendChild($email);          
        }
        
        // add org name element
        $org = $doc->createElement('gd:organization');
        $org->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
        $entry->appendChild($org);
        $orgName = $doc->createElement('gd:orgName', $inputData['org']);
        $org->appendChild($orgName);
        
        // insert entry
        $entryResult = $gdata->insertEntry($doc->saveXML(), 
         'http://www.google.com/m8/feeds/contacts/default/full');
        echo 'The ID of the new entry is: ' . $entryResult->id;
      } catch (Exception $e) {
        die('ERROR:' . $e->getMessage());
      }
    }
    ?>
  </body>
</html>

Listing 10 actually consists of two parts: a Web form, and the PHP code that processes the input submitted through the form. Figure 5 illustrates what this form looks like.

Figure 5. A Web form to add a new contact
Screen capture of a Web page with form fields to add a new contact

Once the user enters a contact's name, e-mail address, and organization into this form and submits it, the second half of the script swings into action. First, an HTTP client is initialized and used to open an authenticated connection to the Google Contacts Data API. Next, the input entered into the Web form is validated, and a new DOMDocument object is initialized to hold the data for the new entry.

The DOM methods in PHP then dynamically construct a new <entry>, and the insertEntry() method actually saves the entry to the Google servers. Once the entry is added, the API will return its unique ID, which displays to the user to indicate a successful operation.

Figure 6 illustrates the output after successfully adding a new entry.

Figure 6. The output after adding a new contact
Screen capture of the output after you add a new contact

In the screen capture in Figure 6, some fields are masked in the confirmation message from Google. The message is in the format: The ID of the new entry is http://www.google.com/m8/feeds/contacts/USER_ID/base/CONTACT_ID.


Conclusion

In this article, you took a crash course on how to integrate data from the Google Contacts Data API into a PHP application using a combination of SimpleXML and the Zend client library. The examples in this article introduced you to the Google Contacts feed format; showed you how to retrieve contact listings; add, modify, and delete contacts; and build a customized interface to the contact information data stored in a user's Google Account.

As these examples illustrate, the Google Contacts Data API is a powerful and flexible tool for developers looking to build creative new applications around contact management. Play with it sometime, and see what you think!


Download

DescriptionNameSize
Archive of the final code examplecontacts-example-app.zip4KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Open source
ArticleID=429101
ArticleTitle=Integrate your PHP application with Google Contacts
publish-date=09222009