As someone who takes a lot of digital photos, I'm no stranger to disk clutter—my computer has hundreds of megabytes of old photos, most with sketchy or non-existent labels. Only a few months ago, when I finally tried out Google's free Picasa photo management software, some method began to emerge from the madness.
But Picasa is more than just a tool you can download off the Web—it also exists online, as a free photo-sharing site called Picasa Web Albums. At the Web site, users can upload photos and videos, categorize them into albums, tag them with keywords for easy searching, and share them with others. The best part, though, is behind the scenes: Like many other Google tools, Picasa Web Albums exposes a Data API which allows developers to build customized applications around the images stored in user photo albums. You can access this API, which follows the REST model, through any XML-capable development toolkit. The API already has client libraries for many common programming languages...including the one I use most often, PHP.
This article will introduce you to the Google Picasa Web Albums Data API, and show you how to integrate and use your Picasa albums and photos with PHP applications. Its examples include how to retrieve albums and photos, upload photos through a customized Web interface, and search for photos by keyword. So come on in, and get started!
Understanding the Picasa Web Albums Data API
Before you dive into the PHP code, a few words about the Picasa Web Albums Data API are in order. As with all REST-based services, things start with an HTTP request to a designated resource. This HTTP request contains a query with one or more input parameters; the server replies to the query with an Atom- or RSS-formatted response (or feed), suitable for parsing in any XML-aware client.
A typical Picasa feed contains a lot of information. To see an example, try popping the URL http://picasaweb.google.com/data/feed/api/user/userid?kind=photo into your Web browser (after replacing userid with your Google Account username) and, assuming you already have some photos and albums saved in Picasa Web Albums, you should see something similar to Listing 1.
Take a quick glance through this output to familiarize yourself with its main elements:
- The Picasa Web Albums Data API responds to a REST request with a feed of the requested data. Various feeds are available: album feeds, photo feeds, user feeds, user contact feeds, and so on. In most cases, therefore, the XML response contains a <feed> element as the root element. The <feed> element contains <link> elements, which contain URLs for the current, next, and previous pages of the result set, and <openSearch:> elements, which contain summary statistics for the search.
- The outermost <feed> element encloses one or more <entry> elements, each representing a photo matching the query. Each <entry> contains further information on the photo it represents, including the caption, file name, publication date, and author. Each <entry> also contains <link> elements, which provide URL links to view and edit the photo.
- A set of <gphoto:> elements within each <entry> contain detailed information on the photo: its size, dimensions, album, comment count, and so on.
- A <media:group> element within each <entry> contains photo keywords and thumbnail links.
Retrieving photos with SimpleXML
Now you'll proceed to an example of parsing a Picasa Web Albums feed with PHP and SimpleXML. 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 photo listings with SimpleXML
<!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 album contents</title>
<style>
body {
font-family: Verdana;
}
h2 {
color: red;
text-decoration: none;
}
span.attr {
font-weight: bolder;
}
img {
float: left;
}
</style>
</head>
<body>
<?php
$userid = 'userid%40googlemail.com';
// build feed URL
$feedURL = "http://picasaweb.google.com/data/feed/api/user/$userid?kind=photo";
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
// get album name and number of photos
$counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
$total = $counts->totalResults;
?>
<h1><?php echo $sxml->title; ?></h1>
<?php echo $total; ?> photo(s) found.
<p/>
<?php
// iterate over entries in album
// print each entry's title, size, dimensions, tags, and thumbnail image
foreach ($sxml->entry as $entry) {
$title = $entry->title;
$summary = $entry->summary;
$gphoto = $entry->children('http://schemas.google.com/photos/2007');
$size = $gphoto->size;
$height = $gphoto->height;
$width = $gphoto->width;
$media = $entry->children('http://search.yahoo.com/mrss/');
$thumbnail = $media->group->thumbnail[1];
$tags = $media->group->keywords;
echo "<h2>$summary</h2>\n";
echo "<table><tr><td><img src=\"" .
$thumbnail->attributes()->{'url'} . "\"/></td>\n";
echo "<td><span class=\"attr\">File</span>: $title
<br />\n";
echo "<span class=\"attr\">Size</span>: $size bytes
($height x $width) <br />\n";
echo "<span class=\"attr\">Tags</span>: $tags
</td></tr></table>\n";
}
?>
</body>
</html>
|
Figure 1 demonstrates the output you might see:
Figure 1. A Web page listing photos

Listing 2 begins by using the simplexml_load_file() object to send a request to the feed URL and convert the response into a SimpleXML object. It then iterates over the <entry> elements in the response, processing each one using a foreach() loop and retrieving the information shown in Figure 1. Child nodes under each <entry> are represented as SimpleXML object properties—for example, the <title> node is represented by $entry->title, the <summary> node by $entry->summary, and so on. Keywords and a thumbnail image are obtained from the <media:group> element under each <entry>.
Working with user and album feeds
If you want to obtain a listing of all the albums created by a particular user, this too is easily managed. You use the feed URL in Listing 2, but alter the kind parameter to list albums instead of photos. Listing 3 demonstrates how to obtain and parse such a feed:
Listing 3: Retrieving album listings with SimpleXML
<!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 albums</title>
<style>
body {
font-family: Verdana;
}
</style>
</head>
<body>
<h1>Album Listing</h2>
<?php
$userid = 'userid%40googlemail.com';
// build feed URL
$feedURL = "http://picasaweb.google.com/data/feed/api/user/$userid?kind=album";
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
// get album names and number of photos in each
echo "<ul>";
foreach ($sxml->entry as $entry) {
$title = $entry->title;
$gphoto = $entry->children('http://schemas.google.com/photos/2007');
$numphotos = $gphoto->numphotos;
echo "<li>$title - $numphotos photo(s)</li>\n";
}
echo "</ul>";
?>
</body>
</html>
|
Figure 2 demonstrates the revised output:
Figure 2. A Web page listing albums

To obtain an album feed, which lists only the photos in a specific album, specify the album name in the feed URL. Listing 4 demonstrates how to obtain and parse such a feed:
Listing 4: Retrieving photos in an album with SimpleXML
<!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 album contents</title>
<style>
body {
font-family: Verdana;
}
h2 {
color: red;
text-decoration: none;
}
span.attr {
font-weight: bolder;
}
img {
float: left;
}
</style>
</head>
<body>
<?php
$userid = 'userid%40googlemail.com';
$album = 'France2008';
// build feed URL
$feedURL = "http://picasaweb.google.com/data/feed/api/user/$userid/album/$album";
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
// get album name and number of photos
$counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
$total = $counts->totalResults;
?>
<h1><?php echo $sxml->title; ?></h1>
<?php echo $total; ?> photo(s) found.
<p/>
<?php
// iterate over entries in album
// print each entry's title, size, dimensions, tags, and thumbnail image
foreach ($sxml->entry as $entry) {
$title = $entry->title;
$summary = $entry->summary;
$gphoto = $entry->children('http://schemas.google.com/photos/2007');
$size = $gphoto->size;
$height = $gphoto->height;
$width = $gphoto->width;
$media = $entry->children('http://search.yahoo.com/mrss/');
$thumbnail = $media->group->thumbnail[1];
$tags = $media->group->keywords;
echo "<h2>$summary</h2>\n";
echo "<table><tr><td><img src=\"" .
$thumbnail->attributes()->{'url'} . "\"/></td>\n";
echo "<td><span class=\"attr\">File</span>: $title
<br />\n";
echo "<span class=\"attr\">Size</span>: $size bytes
($height x $width) <br />\n";
echo "<span class=\"attr\">Tags</span>: $tags
</td></tr></table>\n";
}
?>
</body>
</html>
|
And Figure 3 demonstrates what the output looks like:
Figure 3. A Web page listing photos in an album

Retrieving photos with the Zend GData Client Library
The previous examples have demonstrated how easy it is to read and use data from Picasa Web Albums using PHP. However, read-only access has its limitations. To perform more sophisticated operations that require write access, such as adding new photos or albums, you need to add user authentication to your application 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 trying to 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, and encapsulates most common tasks (including authentication), thus leaving you free to focus on the core functions of your application.
Listing 5 demonstrates, by replicating the functionality of Listing 4 with the Zend GData Client Library:
Listing 5: Retrieving photos in an album with the Zend GData Client Library
<!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 album contents</title>
<style>
body {
font-family: Verdana;
}
h2 {
color: red;
text-decoration: none;
}
span.attr {
font-weight: bolder;
}
img {
float: left;
}
</style>
</head>
<body>
<?php
// load library
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');
// create authenticated HTTP client for Picasa service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);
// generate query to get album feed
$query = $gphoto->newAlbumQuery();
$query->setUser("default");
$query->setAlbumName("France2008");
// get and parse album feed
// get album name and number of photos
try {
$feed = $gphoto->getAlbumFeed($query);
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getResponse();
}
?>
<h1><?php echo $feed->getTitle(); ?></h1>
<?php echo $feed->getTotalResults(); ?> photo(s) found.
<p/>
<?php
// process each photo entry
// print each entry's title, size, dimensions, tags, and thumbnail image
foreach ($feed as $entry) {
$title = $entry->getTitle();
$summary = $entry->getSummary();
$thumbnail = $entry->getMediaGroup()->getThumbnail();
$tags = $entry->getMediaGroup()->getKeywords();
$size = $entry->getGphotoSize();
$height = $entry->getGphotoHeight();
$width = $entry->getGphotoWidth();
echo "<h2>$summary</h2>\n";
echo "<table><tr><td><img src=\"" .
$thumbnail[1]->url . "\"/></td>\n";
echo "<td><span class=\"attr\">File</span>: $title
<br />\n";
echo "<span class=\"attr\">Size</span>: $size bytes
($height x $width) <br />\n";
echo "<span class=\"attr\">Tags</span>: $tags
</td></tr></table>\n";
}
?>
</body>
</html>
|
Listing 5 starts by loading the Zend class libraries, and
then initializes an instance of the Zend_Http_Client class. This client is
provided with the necessary user authentication information, and opens an
authenticated connection to the Picasa service. Once an authenticated connection is opened, the getAlbumFeed() method is used to retrieve the album feed. This method accepts an AlbumQuery object, which is configured with two parameters:
- The owner's Picasa user name
- The album name
The response to the getAlbumFeed() API call is an XML feed similar to that in Listing 1, which is then parsed and converted into a PHP object. This object exposes getter and setter magic methods, making it easy to retrieve specific pieces of information from the nodes in the XML feed through object/property notation. Thus, for example, the getSummary() method retrieves the photo summary, while the getGphotoSize() method retrieves the photo size in bytes.
Figure 4 demonstrates the output you might see:
Figure 4. A Web page listing photos in an album

Now that you can list photos, how about adding new photos? This is actually not as complicated as it might seem.
To add a new photo, simply POST the image file to the album feed URL, together with the associated metadata, as a MIME-encoded block. One part of the block should contain the XML-encoded <entry>; the other, the binary data. Listing 6 has an example of one such block:
Listing 6: An example POST packet for adding a new photo with metadata
--=_00fb9c5957c74e05312cbc2f9d1d09b8
Content-Type: application/atom+xml
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
<atom:category term="http://schemas.google.com/photos/2007#photo"
scheme="http://schemas.google.com/g/2005#kind"/>
<atom:summary type="text">Notre Dame, Paris</atom:summary>
<media:group xmlns:media="http://search.yahoo.com/mrss/">
<media:keywords>church, paris</media:keywords>
</media:group>
</atom:entry>
--=_00fb9c5957c74e05312cbc2f9d1d09b8
Content-Type: image/jpeg
[binary data]
|
If you use the Zend library, things are even simpler. You only need to call
the insertPhotoEntry() method, which creates the
POST packet in Listing 6 and then posts it to the album
feed. Listing 7 demonstrates, as it builds a Web form that
asks the user for the photo file, title, and tags, and then uses the Zend library to construct the corresponding POST packet and save the data to an album:
Listing 7: Adding a new photo
<!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 photos to an album</title>
<style>
body {
font-family: Verdana;
}
li {
border-bottom: solid black 1px;
margin: 10px;
padding: 2px;
width: auto;
padding-bottom: 20px;
}
h2 {
color: red;
text-decoration: none;
}
span.attr {
font-weight: bolder;
}
</style>
</head>
<body>
<h1>Add Photo</h1>
<?php if (!isset($_POST['submit'])) { ?>
<form method="post" action="<?php
echo htmlentities($_SERVER['PHP_SELF']); ?>" enctype="multipart/form-data">
Title: <br/>
<input name="title" type="text" size="25" /><p/>
File to upload: <br/>
<input name="photofile" type="file" /><p/>
Tags: <br/>
<input name="tags" type="text" size="25" /><p/>
<input name="submit" type="submit" value="Save" />
</form>
<?php
} else {
// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');
// connect to service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);
// validate input
if (empty($_POST['title'])) {
die('ERROR: Missing title');
}
// sanitize input
$title = htmlentities($_POST['title']);
$tags = htmlentities($_POST['tags']);
// set album name
$albumName = "France2008";
// construct photo object
// save to server
try {
$photo = $gphoto->newPhotoEntry();
// set file
$file = $gphoto->newMediaFileSource($_FILES['photofile']['tmp_name']);
$file->setContentType("image/jpeg");
$photo->setMediaSource($file);
// set title
$photo->setSummary($gphoto->newSummary($title));
// set tags
$photo->mediaGroup = new Zend_Gdata_Media_Extension_MediaGroup();
$keywords = new Zend_Gdata_Media_Extension_MediaKeywords();
$keywords->setText($tags);
$photo->mediaGroup->keywords = $keywords;
// link to album
$album = $gphoto->newAlbumQuery();
$album->setUser($user);
$album->setAlbumName($albumName);
// save photo
$gphoto->insertPhotoEntry($photo, $album->getQueryUrl());
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getResponse();
}
echo 'Photo successfully added!';
}
?>
</body>
</html>
|
Listing 7 actually consists of two parts:
- A Web form
- 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 photo

Once the user selects an image file for upload, enters a title and optional tags into this form and submits it, the second half of the script swings into action. First, an HTTP client is initialized and opens an authenticated connection to the Picasa Web Albums Data API. Next, the input entered into the Web form is validated, and a new PhotoEntry() object is initialized, to hold the data for the new photo.
The keywords and title entered into the form are then attached to this object
through its mediaGroup property and setSummary() method, respectively. The special file upload array in
PHP, $_FILES, obtains the file name of the uploaded
file, and the setMediaSource() method attaches this
file to the object. Finally, the setAlbumName()
method specifies the album to which the photo is linked (set the album value
to default to place the image in the default album, or drop box).
Once you configure these various object parameters, the insertPhotoEntry() method actually saves the photo and metadata to
the Google servers. After the photo is added, you should immediately see it in the corresponding album.
Figure 6 illustrates the output after successfully adding a new photo:
Figure 6. The result of adding a new photo

To delete a photo, send a DELETE request to the unique URL of the photo. In the
Zend library context, you construct the photo URL using the photo and album
IDs, obtain a reference to the photo (as an object) using the getPhotoEntry() method, and then call the delete() method of that object. Listing 8 illustrates the process:
Listing 8: Deleting a photo
<?php
// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');
// connect to service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);
// get photo entry
// delete entry
try {
$photo = $gphoto->getPhotoEntry( 'http://picasaweb.google.com/data/entry/api/
user/xxxxx/albumid/yyyyy/photoid/zzzzz');
$photo->delete();
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getResponse();
}
echo 'Photo successfully deleted!';
?>
|
To edit a photo, use the getPhotoEntry() method
again. This time, set new values for the various object properties and then
call the save() method of the object to save the changes back to the server. Listing 9 illustrates the process:
Listing 9: Updating a photo
<?php
// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');
// connect to service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);
// get photo entry
// delete entry
try {
$photo = $gphoto->getPhotoEntry('http://picasaweb.google.com/
data/entry/api/user/xxxxx/albumid/yyyyy/photoid/zzzzz');
// set new title
$photo->setSummary($gphoto->newSummary('New title'));
// set new tags
$photo->mediaGroup = new Zend_Gdata_Media_Extension_MediaGroup();
$keywords = new Zend_Gdata_Media_Extension_MediaKeywords();
$keywords->setText('new,tags');
$photo->mediaGroup->keywords = $keywords;
$photo->save();
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getResponse();
}
echo 'Photo successfully modified!';
?>
|
The Picasa Web Albums Data API also lets you customize feeds. You can either
restrict the number of entries in the feed, or retrieve only those entries
that match search criteria. For example, as Listing 10
illustrates, if you add the 'max-results' parameter
to the feed URL, you can limit the number of entries returned in the feed response:
Listing 10: Restricting feed results
<!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>Restricting feed results</title>
<style>
body {
font-family: Verdana;
}
h2 {
color: red;
text-decoration: none;
}
span.attr {
font-weight: bolder;
}
img {
float: left;
}
</style>
</head>
<body>
<?php
$userid = 'userid%40googlemail.com';
$album = 'France2008';
// build feed URL
$feedURL = "http://picasaweb.google.com/data/feed/api/
user/$userid/album/$album?max-results=3";
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
// get album name and number of photos
$counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
$total = $counts->totalResults;
?>
<h1><?php echo $sxml->title; ?></h1>
<?php echo $total; ?> photo(s) found.
<p/>
<?php
// iterate over entries in album
// print each entry's title, size, dimensions, tags, and thumbnail image
foreach ($sxml->entry as $entry) {
$title = $entry->title;
$summary = $entry->summary;
$gphoto = $entry->children('http://schemas.google.com/photos/2007');
$size = $gphoto->size;
$height = $gphoto->height;
$width = $gphoto->width;
$media = $entry->children('http://search.yahoo.com/mrss/');
$thumbnail = $media->group->thumbnail[1];
$tags = $media->group->keywords;
echo "<h2>$summary</h2>\n";
echo "<table><tr><td><img src=\"" .
$thumbnail->attributes()->{'url'} . "\"/></td>\n";
echo "<td><span class=\"attr\">File</span>: $title
<br />\n";
echo "<span class=\"attr\">Size</span>: $size
($height x $width) <br />\n";
echo "<span class=\"attr\">Tags</span>: $tags
</td></tr></table>\n";
}
?>
</body>
</html>
|
Alternatively, you can retrieve only those entries that match a particular
query string, when you use the q parameter. Picasa
will search the title, caption, and tags of each photo for matches to the query string. Listing 11 illustrates by displaying all photos with the tag 'church':
Listing 11: Searching for photos by keyword
<!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>Searching photos by tag</title>
<style>
body {
font-family: Verdana;
}
h2 {
color: red;
text-decoration: none;
}
span.attr {
font-weight: bolder;
}
img {
float: left;
}
</style>
</head>
<body>
<?php
$userid = 'userid%40googlemail.com';
$album = 'France2008';
// build feed URL
$feedURL = "http://picasaweb.google.com/data/feed/api/user/
$userid/album/$album?tag=church";
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
// get album name and number of photos
$counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
$total = $counts->totalResults;
?>
<h1><?php echo $sxml->title; ?></h1>
<?php echo $total; ?> photo(s) found.
<p/>
<?php
// iterate over entries in album
// print each entry's title, size, dimensions, tags and thumbnail image
foreach ($sxml->entry as $entry) {
$title = $entry->title;
$summary = $entry->summary;
$gphoto = $entry->children('http://schemas.google.com/photos/2007');
$size = $gphoto->size;
$height = $gphoto->height;
$width = $gphoto->width;
$media = $entry->children('http://search.yahoo.com/mrss/');
$thumbnail = $media->group->thumbnail[1];
$tags = $media->group->keywords;
echo "<h2>$summary</h2>\n";
echo "<table><tr><td><img src=\"" .
$thumbnail->attributes()->{'url'} . "\"/></td>\n";
echo "<td><span class=\"attr\">File</span>: $title
<br />\n";
echo "<span class=\"attr\">Size</span>: $size
($height x $width) <br />\n";
echo "<span class=\"attr\">Tags</span>: $tags
</td></tr></table>\n";
}
?>
</body>
</html>
|
Figure 7 illustrates the likely output:
Figure 7. Searching for photos by keyword

You're ready to put all this information to practical use, and build a simple PHP-based application that allows you to view, delete, add, and search for photos in a given album, piggy-backing on the features offered by the Picasa Web Albums Data API. This prototype application has 3 scripts (see in Download):
- 'list.php'—Lists photos in an album, and provides links to search, add, and delete photos
- 'add.php'—Displays a form to add a new photo and processes the submission
- 'delete.php'—Deletes a specified photo
Begin with 'list.php' (Listing 12), which updates Listing 4 so that each photo is now accompanied by a delete link. The page also contains an additional search form:
Listing 12: Listing photos
<!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 album contents</title>
<style>
body {
font-family: Verdana;
}
h2 {
color: red;
text-decoration: none;
}
span.attr {
font-weight: bolder;
}
img {
float: left;
}
</style>
</head>
<body>
<?php
// load library
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');
// create authenticated HTTP client for Picasa service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);
// generate query to get album feed
$query = $gphoto->newAlbumQuery();
$query->setUser("default");
$query->setAlbumName("France2008");
// filter by search term
if(isset($_GET['q'])) {
$query->setQuery($_GET['q']);
}
// get and parse album feed
// get album name and number of photos
try {
$feed = $gphoto->getAlbumFeed($query);
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getResponse();
}
?>
<h1><?php echo $feed->getTitle(); ?></h1>
<?php echo $feed->getTotalResults(); ?> photo(s) found.
<p/>
<?php
// process each photo entry
// print each entry's title, size, dimensions, tags, and thumbnail image
foreach ($feed as $entry) {
$title = $entry->getTitle();
$summary = $entry->getSummary();
$thumbnail = $entry->getMediaGroup()->getThumbnail();
$tags = $entry->getMediaGroup()->getKeywords();
$size = $entry->getGphotoSize();
$height = $entry->getGphotoHeight();
$width = $entry->getGphotoWidth();
$photoid = $entry->getGphotoId();
$albumid = $entry->getGphotoAlbumId();
echo "<h2>$summary</h2>\n";
echo "<table><tr><td><img src=\"" .
$thumbnail[1]->url . "\"/></td>\n";
echo "<td><span class=\"attr\">File</span>: $title
<br />\n";
echo "<span class=\"attr\">Size</span>: $size bytes
($height x $width) <br />\n";
echo "<span class=\"attr\">Tags</span>: $tags
<br />\n";
echo "<a href=\"delete.php?aid=$albumid&pid=$photoid\">
delete photo</a></td></tr></table>\n";
}
?>
<a href="add.php">Add a new photo</a>
<p/>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
Search for photos containing:<br/>
<input type="text" name="q" size="10"/><p/>
<input type="submit" name="submit" value="Search"/>
</form>
</body>
</html>
|
Figure 8 illustrates what you might see after you run this script:
Figure 8. A Web page listing photos in an album

Notice from Listing 12 that the search form, on submission, simply re-invokes list.php and attaches the user's query string to the requested feed, through the setQuery() method of the query object.
The add.php script adds new photos, as illustrated in Listing 13:
Listing 13: Adding photos
<!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 photos to an album</title>
<style>
body {
font-family: Verdana;
}
li {
border-bottom: solid black 1px;
margin: 10px;
padding: 2px;
width: auto;
padding-bottom: 20px;
}
h2 {
color: red;
text-decoration: none;
}
span.attr {
font-weight: bolder;
}
</style>
</head>
<body>
<h1>Add Photo</h1>
<?php if (!isset($_POST['submit'])) { ?>
<form method="post" action="<?php
echo htmlentities($_SERVER['PHP_SELF']); ?>" enctype="multipart/form-data">
Title: <br/>
<input name="title" type="text" size="25" /><p/>
File to upload: <br/>
<input name="photofile" type="file" /><p/>
Tags: <br/>
<input name="tags" type="text" size="25" /><p/>
<input name="submit" type="submit" value="Save" />
</form>
<?php
} else {
// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');
// connect to service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);
// validate input
if (empty($_POST['title'])) {
die('ERROR: Missing title');
}
// sanitize input
$title = htmlentities($_POST['title']);
$tags = htmlentities($_POST['tags']);
// set album name
$albumName = "France2008";
// construct photo object
// save to server
try {
$photo = $gphoto->newPhotoEntry();
// set file
$file = $gphoto->newMediaFileSource($_FILES['photofile']['tmp_name']);
$file->setContentType("image/jpeg");
$photo->setMediaSource($file);
// set title
$photo->setSummary($gphoto->newSummary($title));
// set tags
$photo->mediaGroup = new Zend_Gdata_Media_Extension_MediaGroup();
$keywords = new Zend_Gdata_Media_Extension_MediaKeywords();
$keywords->setText($tags);
$photo->mediaGroup->keywords = $keywords;
// link to album
$album = $gphoto->newAlbumQuery();
$album->setUser($user);
$album->setAlbumName($albumName);
// save photo
$gphoto->insertPhotoEntry($photo, $album->getQueryUrl());
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getResponse();
}
echo 'Photo successfully added!';
}
?>
</body>
</html>
|
Figure 9 illustrates this form:
Figure 9. A Web form to add new photos

You delete photos with the delete.php script, which accepts the unique album and photo IDs from list.php using the GET method. The delete.php script uses this information to locate and delete the photo entry. Listing 14 shows the code:
Listing 14: Deleting photos
<?php
// check for album and photo ID
if (empty($_GET['aid']) || empty($_GET['pid'])) {
die('ERROR: Missing input parameters');
} else{
$aid = htmlentities($_GET['aid']);
$pid = htmlentities($_GET['pid']);
}
// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');
// connect to service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);
// get photo entry
// delete entry
try {
$photo = $gphoto->getPhotoEntry('http://picasaweb.google.com/data/
entry/api/user/userid/albumid/' . $aid . '/photoid/' . $pid);
$photo->delete();
} catch (Zend_Gdata_App_Exception $e) {
echo "Error: " . $e->getResponse();
}
echo 'Photo successfully deleted!';
?>
|
Over the last few pages, you walked through a crash course in how to integrate data from the Picasa Web Albums service into a PHP application using both SimpleXML and the Zend client library. The examples in this article introduced you to the Picasa feed format showed you how to:
- Retrieve album and photo listings
- Filter these listings by search terms; add, modify and delete photos; and build a customized interface to the Picasa service
- Add, modify and delete photos
- Build a customized interface to the Picasa service
As these examples illustrate, the Picasa Web Albums Data API is a powerful and flexible tool for developers who want to build creative new applications around photo management and storage. Play with it sometime, and see what you think!
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code for article | x-picasalbum-example.zip | 4KB | HTTP |
Information about download methods
Learn
- Picasa Web Albums Data API Developer's
Guide and Reference
Guide: Learn more about the Picasa Web Albums APIs and what you can do with the Data
API.
- Zend Framework Manual: Discover more about Zend's GData API for Picasa Web Albums.
- The
Google Data APIs developer blog: Track the latest API news.
- The Picasa Web Albums API discussion forum: Ask questions and find answers.
- Create a Google Account: Register for a Google account and get started.
-
Using Picasa Web Albums Feeds in Google Mashup Editor (Lev Epshteyn, GME Team
August 2007): Read an article on creating mashups with Picasa Web Albums Feeds.
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
- The technology
bookstore: Browse for books on these and other technical topics.
- developerWorks
podcasts: Listen to interesting interviews and discussions for software developers.
Get products and technologies
- the Zend GData Client Library: Download the Zend_Gdata PHP 5 client interfaces for the Google Data APIs.
- IBM
trial software for product evaluation: Build your next project with trial software available for download directly from developerWorks, including application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
- XML zone discussion forums: Participate in any of several XML-related discussions.
- developerWorks XML zone: Share your thoughts: After you read this article, post your comments and thoughts in this forum. The XML zone editors moderate the forum and welcome your input.
- developerWorks blogs: Check out these blogs and get involved in the developerWorks community.
Vikram 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 How to do Everything with PHP and MySQL.
Comments (Undergoing maintenance)





