使用 PHP 集成 Google Spreadsheets 数据,第 1 部分

利用 PHP 通过一个 web 应用程序处理和集成来自 Google Spreadsheets 的数据提要

Google Spreadsheets 是著名的基于云的电子表格应用程序。Web 应用程序开发人员可以通过 Google Spreadsheet Data API 从在线电子表格中访问和搜索数据。本文将介绍 Google Spreadsheets Data API 并在一个 PHP 应用程序中进行演示,解释如何搜索和检索各种电子表格内容。

Vikram Vaswani, 创始人, Melonfire

Vikram Vaswani 的照片Vikram Vaswani 是 Melonfire 的创始人和 CEO,该公司是一家专门研究开源工具和技术的咨询服务公司。他还著有 PHP Programming SolutionsHow to do Everything with PHP and MySQL 等著作。



2011 年 1 月 17 日

简介

常用缩略词

  • API:应用程序编程接口
  • HTML:超文本标记语言
  • HTTP:超文本传输协议
  • URL:统一资源定位符
  • XML:可扩展标记语言

和大多数人一样,我经常使用电子表格。使用它们可以方便地对账、设置每个月的预算,评估和控制项目进度。然而,大概在一年以前,我开始放弃使用桌面电子表格应用程序,而改为使用 Google Spreadsheets,这是一种基于云的服务,支持所有常用的电子表格特性。这一转变使我享受到了两大好处:

  • 我可以在移动过程中访问我的电子表格数据。
  • 共享和协作式编辑要比以前使用桌面应用程序更加轻松。

从开发人员的角度来看,Google Spreadsheets 之所以引起人们的兴趣,是因为它提供了一个 Data API,允许开发人员围绕用户空间电子表格数据轻松地构建应用程序。您可以通过任何支持 XML 的开发工具访问这个 API,Google 为 PHP 和 Java™ 技术都提供了客户机库。

在这个共两部分的文章中,我将向您介绍 Google Spreadsheets Data API,并通过一个 PHP 应用程序展示如何集成和使用来自云的电子表格数据。 在第一篇文章中,我将介绍 Data API 所支持的各种提要,并解释如何使用 PHP 客户机库从这些提要中读取数据。


理解 Google Spreadsheets 提要

在开始研究 PHP 代码之前,需要理解 Google Spreadsheets Data API 是如何工作的。API 接受包含一个或多个输入参数的 HTTP 请求,然后返回包含所请求信息的 Atom 提要。

Google Spreadsheets Data API 可以支持许多种不同的提要。注意,对于某些提要,需要电子表格 ID (ssid) 或工作表 ID (wsid) 才能创建提要 URL。

  • 电子表格提要包含一个用户可用的电子表格列表。可以通过以下 URL 访问:https://spreadsheets.google.com/feeds/spreadsheets/private/full
  • 工作表提要被绑定到一个特定的电子表格,该电子表格包含一个工作表列表。可以通过 https://spreadsheets.google.com/feeds/worksheets/<ssid>/private/full 之类的 URL 访问。
  • 列表和单元格提要被绑定到一个特定的工作表,其中包含组成工作表的行和单元格。它们可通过 https://spreadsheets.google.com/feeds/list/<ssid>/<wsid>/private/full 之类的 URL 访问。

电子表格只能由经过身份验证的用户访问。根据相应的电子表格的安全设置,也可以在未进行身份验证的情况下访问电子表格提要,如工作表、列表、单元格提要。例如,可以获得一个可以公共访问的、不需要身份验证的列表或单元格提要。

清单 1 演示了一个典型的电子表格提要是什么样子的。

清单 1. 电子表格提要示例
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' 
  xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
  <id>https://spreadsheets.google.com/feeds/spreadsheets/private/full</id>
  <updated>2010-11-05T17:11:33.703Z</updated>
  <category scheme='http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/>
  <title type='text'>Available Spreadsheets - someuser@gmail.com
  </title>
  <link rel='alternate' type='text/html' href='http://docs.google.com'/>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/spreadsheets/private/full'/>
  <link rel='self' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/spreadsheets/private/full'/>
  <openSearch:totalResults>3</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <entry>
    <id>https://spreadsheets.google.com/feeds/spreadsheets/private/full/ssid</id>
    <updated>2010-11-04T07:24:42.388Z</updated>
    <category scheme='http://schemas.google.com/spreadsheets/2006'
      term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/>
    <title type='text'>Overtime Hours</title>
    <content type='text'>Overtime Hours</content>
    <link rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed' 
      type='application/atom+xml' 
      href='https://spreadsheets.google.com/feeds/worksheets/ssid/private/full'/>
    <link rel='alternate' type='text/html' 
      href='https://spreadsheets.google.com/ccc?key'/>
    <link rel='self' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/spreadsheets/private/full/ssid'/>
    <author>
      <name>someuser</name>
      <email>someuser@gmail.com</email>
    </author>
  </entry>
  <entry>
  ...
  </entry>
</feed>

所有电子表格提要都以 <feed> 元素为根元素。<feed> 元素包含一个或多个 <entry> 元素,每个 <entry> 元素都表示一个不同的电子表格。每个 <entry> 包含更多的内容,包括标题、最后更新日期和每个电子表格的作者。

注意,每个 <entry> 还包含一个 <link rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed' ...> 元素。该元素指定电子表格的工作表提要的 URL,并提供一种方法来向下钻取更深一层的细节。请求该 URL 将生成一个电子表格提要,其中包含指定的电子表格中的工作表的信息。清单 2 演示了这类工作表提要的示例。

清单 2. 示例工作表提要
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' 
  xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' 
  xmlns:gs='http://schemas.google.com/spreadsheets/2006'>
  <id>https://spreadsheets.google.com/feeds/worksheets/ssid/private/full</id>
  <updated>2010-11-04T07:23:37.513Z</updated>
  <category scheme='http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#worksheet'/>
  <title type='text'>Estimate: Home Renovation</title>
  <link rel='alternate' type='text/html' href='https://spreadsheets.google.com/ccc
    ?key=ssid'/>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/worksheets/ssid/private/full'/>
  <link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/worksheets/ssid/private/full'/>
  <link rel='self' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/worksheets/ssid/private/full'/>
  <author>
    <name>vikram.melonfire</name>
    <email>someuser@gmail.com</email>
  </author>
  <openSearch:totalResults>7</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <entry>
    <id>https://spreadsheets.google.com/feeds/worksheets/ssid/private/full/wsid
    </id>
    <updated>2010-11-04T07:22:22.440Z</updated>
    <category scheme='http://schemas.google.com/spreadsheets/2006'
      term='http://schemas.google.com/spreadsheets/2006#worksheet'/>
    <title type='text'>Living Room</title>
    <content type='text'>Living Room</content>
    <link rel='http://schemas.google.com/spreadsheets/2006#listfeed' 
      type='application/atom+xml' href='https://spreadsheets.google.com/feeds/list/ssid/
      wsid/private/full'/>
    <link rel='http://schemas.google.com/spreadsheets/2006#cellsfeed' 
      type='application/atom+xml' href='https://spreadsheets.google.com/feeds/cells/ssid/
      wsid/private/full'/>
    <link rel='http://schemas.google.com/visualization/2008#visualizationApi' 
      type='application/atom+xml' href='https://spreadsheets.google.com/tq?
      key=ssid&sheet=wsid'/>
    <link rel='self' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/worksheets/ssid/private/full/wsid'/>
    <link rel='edit' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/worksheets/ssid/private/full/wsid/key'
    />
    <gs:rowCount>100</gs:rowCount>
    <gs:colCount>20</gs:colCount>
  </entry>
  <entry>
  </entry>
</feed>

在工作表提要中,每个 <entry> 元素包含一个 <link> 元素列表。这些元素为工作表的列表提要、单元格提要、可视化端点和编辑端点指定 URL。在浏览或编辑工作表信息时可以用到它们。本文后面将给出一些列表和单元格提要的例子。


检索电子表格

Google Spreadsheets 提要使用 XML 编码,因此可以很容易地将其与 PHP web 应用程序集成。一种方法是在 PHP 中使用 SimpleXML 扩展来解析由 API 返回的 Atom 提要,并从中提取相关信息。但是,Zend Framework 提供了 Zend_Gdata,一种专门供开发人员集成 PHP 应用程序和 Google Data API 的 PHP 客户机库,那么何必再使用上述方法呢?这个库可以单独下载(参见 参考资料 中的链接),为 Google Data API 提供了一个方便、面向对象的界面,其中封装了最常见的任务,包括身份验证,因此您可以将注意力放在应用程序的核心功能上。

安装好 Zend 的 GData 库后,接下来看一看如何使用 PHP 处理 Google Spreadsheets Data API 提要。清单 3 获取了 清单 1 中的提要并使用 Zend_Gdata 从其中提取相关的数据片段,并将其格式化为一个 web 页面。注意,在执行脚本之前,您需要用有效的值更新脚本中的用户凭证。

清单 3. 列出电子表格
<!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 spreadsheets</title>
    <style>
    body {
      font-family: Verdana;      
    }
    </style>    
  </head>
  <body>
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

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

    try {  
      // connect to API
      $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
      $service = new Zend_Gdata_Spreadsheets($client);

      // get list of available spreadsheets
      $feed = $service->getSpreadsheetFeed();
    } catch (Exception $e) {
      die('ERROR: ' . $e->getMessage());
    }
    ?>
  
    <h2><?php echo $feed->title; ?></h2>
    <div>
    <?php echo $feed->totalResults; ?> spreadsheet(s) found.
    </div>  

    <ul>
    <?php foreach($feed as $entry): ?>
      <li class="name"><?php echo $entry->getTitle(); ?></li>
    <?php endforeach; ?>
    </ul>

  </body>
<html>

清单 3 首先加载 Zend 类库,然后初始化 Zend_Gdata 服务类的一个实例。该类将利用 Zend_Http_Client 对象(将向该对象提供必要的用户验证信息)并打开一个经过身份验证的 Google Spreadsheets 服务连接。打开一个已进行身份验证的连接后,getSpreadsheetFeed() 方法将检索电子表格提要。

getSpreadsheetFeed() API 调用的响应是一个与 清单 1 类似的 Atom 提要,该提要被解析并转换为一个 Zend_Gdata_Spreadsheets_SpreadsheetEntry 对象数组,每个对象代表提要中的一个 <entry>。现在可以遍历该数组,通过调用相应对象的 getTitle() 方法输出每个电子表格的标题。

图 1 展示了输出是什么样子的,包含由三个可用电子表格组成的列表(加班小时数,预算:家庭装修,各地区销售情况)。

图 1. 用户电子表格列表
(用户电子表格列表的屏幕截图(加班小时,预算:家庭装修,各地区的销售情况)

检索工作表

每个 Zend_Gdata_Spreadsheets_SpreadsheetEntry 对象还公开一个 getWorksheets() 方法,该方法访问电子表格的工作表提要并返回一个工作表列表作为 Zend_Gdata_Spreadsheets_WorksheetEntry 对象。查看 清单 4,它对 清单 3 进行了修改,列出了电子表格中的工作表。

清单 4. 列出电子表格和工作表
<!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 spreadsheets and worksheets</title>
    <style>
    body {
      font-family: Verdana;      
    }
    </style>    
  </head>
  <body>
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

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

    try {  
      // connect to API
      $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
      $service = new Zend_Gdata_Spreadsheets($client);

      // get list of available spreadsheets
      $feed = $service->getSpreadsheetFeed();
    } catch (Exception $e) {
      die('ERROR: ' . $e->getMessage());
    }
    ?>

    <h2><?php echo $feed->title; ?></h2>
    <div>
    <?php echo $feed->totalResults; ?> spreadsheet(s) found.
    </div>  

    <ul>
    <?php foreach($feed as $entry): ?>
      <li class="name"><?php echo $entry->getTitle();  ?></li>
      <ul>
      <?php foreach($entry->getWorksheets() as $ws): ?>
        <li class="name"><?php echo $ws->getTitle(); ?></li>      
      <?php endforeach; ?>
      </ul>
    <?php endforeach; ?>
    </ul>

  </body>
<html>

注意对每个条目对象的 getWorksheets() 方法的调用。该方法请求每个电子表格的工作表提要并将结果转换为一个 Zend_Gdata_Spreadsheets_WorksheetEntry 对象集合。和之前一样,可以轻松地遍历这个集合并使用 getTitle() 方法显示每个工作表条目的标题。(查看 图 2 的文字说明)。

图 2 解释了修改后的输出。

图 2. 用户电子表格和工作表列表
用户电子表格和工作表列表的屏幕截图

检索工作表所在的行

现在,您已经了解了如何获得电子表格和工作表,现在考虑如何检索工作表的实际内容。Google Spreadsheets Data API 提供了两种实现方式:

  • 列表提要,返回包含工作表所在行的提要
  • 单元格提要,返回包含组成工作表的单元格的提要

为了方便演示,考虑 图 3,其中演示了 “地区销售情况” 电子表格的示例工作表。(该电子表格显示了若干个地区的销售情况,并按照销售量和销售额列出)。

图 3. 本文中使用的示例工作表
本文中使用的示例工作表的屏幕截图

清单 5 显示了与该工作表对应的列表提要。

清单 5. 示例列表提要
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' 
  xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'
  xmlns:gsx='http://schemas.google.com/spreadsheets/2006/extended'>
  <id>https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full</id>
  <updated>2010-11-04T07:21:19.158Z</updated>
  <category scheme='http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#list'/>
  <title type='text'>North</title>
  <link rel='alternate' type='text/html' 
    href='https://spreadsheets.google.com/ccc?key=ssid'/>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full'/>
  <link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full'/>
  <link rel='self' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full'/>
  <author>
    <name>someuser</name>
    <email>someuser@gmail.com</email>
  </author>
  <openSearch:totalResults>3</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <entry>
    <id>https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full/cokwr
    </id>
    <updated>2010-11-04T07:21:19.158Z</updated>
    <category scheme='http://schemas.google.com/spreadsheets/2006'
      term='http://schemas.google.com/spreadsheets/2006#list'/>
    <title type='text'>Sector 1</title>
    <content type='text'>salesunits: 63274, sales: 2214590</content>
    <link rel='self' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full/cokwr'/>
    <link rel='edit' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full/cokwr/
      2ed6e01i15gc7'/>
    <gsx:name>Sector 1</gsx:name>
    <gsx:salesunits>63274</gsx:salesunits>
    <gsx:sales>2214590</gsx:sales>
  </entry>
  <entry>
    <id>https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full/cpzh4
    </id>
    <updated>2010-11-04T07:21:19.158Z</updated>
    <category scheme='http://schemas.google.com/spreadsheets/2006'
      term='http://schemas.google.com/spreadsheets/2006#list'/>
    <title type='text'>Sector 2</title>
    <content type='text'>salesunits: 7263, sales: 254205</content>
    <link rel='self' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full/cpzh4'/>
    <link rel='edit' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full/
      cpzh4/2ed58j9il57e0'/>
    <gsx:name>Sector 2</gsx:name>
    <gsx:salesunits>7263</gsx:salesunits>
    <gsx:sales>254205</gsx:sales>
  </entry>
  <entry>
  ...
  </entry>
</feed>

清单 6 显示了该工作表的单元格提要。

清单 6. 示例单元格提要
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' 
  xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' 
  xmlns:gs='http://schemas.google.com/spreadsheets/2006' 
  xmlns:batch='http://schemas.google.com/gdata/batch'>
  <id>https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full</id>
  <updated>2010-11-04T07:20:36.606Z</updated>
  <category scheme='http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#cell'/>
  <title type='text'>North</title>
  <link rel='alternate' type='text/html' 
    href='https://spreadsheets.google.com/ccc?key=ssid'/>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full'/>
  <link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full'/>
  <link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full/batch'/>
  <link rel='self' type='application/atom+xml'
    href='https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full'/>
  <author>
    <name>vikram.melonfire</name>
    <email>someuser@gmail.com</email>
  </author>
  <openSearch:totalResults>12</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <gs:rowCount>100</gs:rowCount>
  <gs:colCount>20</gs:colCount>
  <entry>
    <id>https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full/R1C1
    </id>
    <updated>2010-11-04T07:20:36.606Z</updated>
    <category scheme='http://schemas.google.com/spreadsheets/2006'
      term='http://schemas.google.com/spreadsheets/2006#cell'/>
    <title type='text'>A1</title>
    <content type='text'>Name</content>
    <link rel='self' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full/R1C1'/>
    <link rel='edit' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full/R1C1/1fvl7'
    />
    <gs:cell row='1' col='1' inputValue='Name'>Name</gs:cell>
  </entry>
  <entry>
    <id>https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full/R1C2
    </id>
    <updated>2010-11-04T07:20:36.606Z</updated>
    <category scheme='http://schemas.google.com/spreadsheets/2006'
      term='http://schemas.google.com/spreadsheets/2006#cell'/>
    <title type='text'>B1</title>
    <content type='text'>Sales (Units)</content>
    <link rel='self' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full/R1C2'/>
    <link rel='edit' type='application/atom+xml'
      href='https://spreadsheets.google.com/feeds/cells/ssid/wsid/private/full/R1C2
      /rkxyni'/>
    <gs:cell row='1' col='2' inputValue='Sales (Units)'>Sales (Units)</gs:cell>
  </entry>
  <entry>
  ...
  </entry>
</feed>

清单 5清单 6 之间的区别应当非常清晰。清单 5 将工作表的每一行表示为一个条目,而 清单 6 将工作表的每个单元格表示为一个条目。根据对工作表数据的处理方式,需要根据用途选择相应的提要。

清单 7 解释了如何使用 Zend_Gdata 检索和处理一个列表提要。对于清单 7 以及后面的清单,需要用电子表格 ID 或工作表 ID(或同时使用两者)更新脚本,即使有实际的值替换清单代码中的 ssidwsid 变量。

清单 7. 处理列表提要
<!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 worksheet contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    div.sheet {
      margin: 10px;
      padding: 10px;
      border: silver 2px dotted;
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }    
    table, td {
      border: 1px solid black;
      vertical-align: top;
    }
    </style>    
  </head>
  <body>
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

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

    try {  
      // connect to API
      $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
      $service = new Zend_Gdata_Spreadsheets($client);

      // get spreadsheet entry
      $ssEntry = $service->getSpreadsheetEntry(
        'https://spreadsheets.google.com/feeds/spreadsheets/ssid');
      
      // get worksheets in this spreadsheet
      $wsFeed = $ssEntry->getWorksheets();
    } catch (Exception $e) {
      die('ERROR: ' . $e->getMessage());
    }    
    ?>

    <h2><?php echo $ssEntry->title; ?></h2>

    <?php foreach($wsFeed as $wsEntry): ?>
    <div class="sheet">
      <div class="name">Worksheet: 
        <?php echo $wsEntry->getTitle(); ?></div>
      <table>
      <?php 
      // get list feed for worksheet
      // get individual entries in list
      $listFeed = $service->getListFeed($wsEntry); 
      foreach ($listFeed as $listEntry) {
        echo '<tr>';
        $rowData = $listEntry->getCustom();
        foreach($rowData as $customEntry) {
          echo '<td>' .  $customEntry->getText() . '</td>';
        }
        echo '</tr>';
      }
      ?>
      </table>
    </div>
    <?php endforeach; ?>

  </body>
<html>

清单 7 使用 清单 4 中的 getWorksheets() 方法,以 Zend_Gdata_Spreadsheets_WorksheetEntry 对象集合的方式检索指定电子表格的所有工作表。服务对象的 getListFeed() 方法随后遍历这些条目对象,以检索与每个工作表对应的列表提要。

组成工作表的行被表示为 Zend_Gdata_Spreadsheets_ListEntry 对象,每个对象公开一个 getCustom() 方法。该方法以数组形式返回每个行的各个元素(单元格)。此时,可以非常轻松地遍历这个数组,并在一个表中输出每个单元格的内容。

图 4 显示了检索结果,每个工作表(北方、南方、东部)分别列出了各个地区的销售量和销售额。

图 4. 从列表提要中检索到的工作表内容
从列表提要中检索到的工作表内容的屏幕截图

检索工作表单元格

正如可以使用服务对象的 getListFeed() 方法获得列表提要一样,也可以使用 getCellFeed() 方法获得单元格提要。参见 清单 8

清单 8. 处理单元格提要
<!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 worksheet contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    div.sheet {
      margin: 10px;
      padding: 10px;
      border: silver 2px dotted;
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }    

    </style>    
  </head>
  <body>
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

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

    try {  
      // connect to API
      $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
      $service = new Zend_Gdata_Spreadsheets($client);

      // get spreadsheet entry
      $ssEntry = $service->getSpreadsheetEntry(
        'https://spreadsheets.google.com/feeds/spreadsheets/ssid');

      // get worksheets in this spreadsheet
      $wsFeed = $ssEntry->getWorksheets();
    } catch (Exception $e) {
      die('ERROR: ' . $e->getMessage());
    }    
    ?>
  
    <h2><?php echo $ssEntry->title; ?></h2>

    <?php foreach($wsFeed as $wsEntry): ?>
    <div class="sheet">
      <div class="name">Worksheet: 
        <?php echo $wsEntry->getTitle(); ?></div>
      <table>
      <?php 
      // get cell feed for worksheet
      // display individual rows and columns from cell feed
      $cellFeed = $service->getCellFeed($wsEntry); 
      echo '<ul>';
      foreach ($cellFeed as $cellEntry) {
        $row = $cellEntry->getCell()->getRow();
        $column = $cellEntry->getCell()->getColumn();
        $value = $cellEntry->getCell()->getText();
        echo "<li>($row, $column) = $value</li>";
      }
      echo '</ul>';
      ?>
      </table>
    </div>
    <?php endforeach; ?>

  </body>
<html>

清单 8 处理一个单元格提要,因此从单元格提要提取数据的方法与 清单 7 中的方法稍有不同。考虑 清单 8,工作表的各个单元格被表示为 Zend_Gdata_Spreadsheets_CellEntry 对象,每个对象公开一个 getCell() 方法,该方法返回一个 Cell 对象。这个 cell 对象反过来又公开一些方法来返回行号、列号和对应单元格的值。现在可以很轻松地遍历这个集合并在一个列表中输出每个单元格的内容。

图 5 显示了输出。

图 5. 从单元格提要提取的工作表内容
从单元格提要提取的工作表内容的屏幕截图

以数组形式检索工作表数据

Zend_Gdata_Spreadsheets 还提供了一些快捷方法,可以将列表和单元格提要转换为原生 PHP 数组以简化处理。例如,getContentsAsRows() 方法检索工作表的列表提要并将其转化为一个嵌套的 PHP 数组。清单 9 解释了这一过程。

清单 9. 处理工作表行数组
<!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 worksheet contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    div.sheet {
      margin: 10px;
      padding: 10px;
      border: silver 2px dotted;
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }    
    table, td {
      border: 1px solid black;
      vertical-align: top;
    }
    </style>    
  </head>
  <body>
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

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

    try {  
      // connect to API
      $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
      $service = new Zend_Gdata_Spreadsheets($client);

      // get spreadsheet entry
      $ssEntry = $service->getSpreadsheetEntry(
        'https://spreadsheets.google.com/feeds/spreadsheets/ssid');
      
      // get worksheets in this spreadsheet
      $wsFeed = $ssEntry->getWorksheets();
    } catch (Exception $e) {
      die('ERROR: ' . $e->getMessage());
    }
    ?>
  
    <h2><?php echo $ssEntry->title; ?></h2>

    <?php foreach($wsFeed as $wsEntry): ?>
    <div class="sheet">
      <div class="name">Worksheet: 
        <?php echo $wsEntry->getTitle(); ?></div>
      <?php $rows = $wsEntry->getContentsAsRows(); ?>
      <table>
      <?php foreach ($rows as $row): ?>
        <tr>
          <?php foreach($row as $key => $value): ?>
          <td><?php echo $value; ?></td>
          <?php endforeach; ?>
        </tr>
      <?php endforeach; ?>
      </table>
    </div>
    <?php endforeach; ?>

  </body>
<html>

清单 9 遍历 getWorksheets() 方法返回的 Zend_Gdata_Spreadsheets_WorksheetEntry 对象的集合,并调用每个条目对象的 getContentsAsRows() 方法,以嵌套数组的形式检索每个工作表行的内容。每一行都被表示为键值对关联数组。

图 6 显示了输出。

图 6. 从列表提要数组检索到的工作表内容
从列表提要数组检索到的工作表内容的屏幕截图

如果在工作中主要使用单元格数据,那么将对 getContentsAsRows() 的调用替换为对 getContentsAsCells() 的调用,如 清单 10 所示。该方法将返回嵌套的单元格数据数组,其中每个单元格使用它的行坐标和列坐标进行索引。每个单元格的公式和最终值也包含在嵌套数组中。

清单 10. 处理工作表单元格数组
<!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 spreadsheet contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    div.sheet {
      margin: 10px;
      padding: 10px;
      border: silver 2px dotted;
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

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

    try {  
      // connect to API
      $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
      $service = new Zend_Gdata_Spreadsheets($client);

      // get spreadsheet entry
      $ssEntry = $service->getSpreadsheetEntry(
        'https://spreadsheets.google.com/feeds/spreadsheets/ssid');

      // get worksheets in this spreadsheet
      $wsFeed = $ssEntry->getWorksheets();
    } catch (Exception $e) {
      die('ERROR: ' . $e->getMessage());
    }
    ?>
  
    <h2><?php echo $ssEntry->title; ?></h2>

    <?php foreach($wsFeed as $wsEntry): ?>
    <div class="sheet">
      <div class="name">Worksheet: 
        <?php echo $wsEntry->getTitle(); ?></div>
      <?php $cells = $wsEntry->getContentsAsCells(); ?>
      <ul>
      <?php foreach ($cells as $location => $data): ?>
        <li>
          <?php echo $location; ?> = <?php echo $data['value']; ?>
          <?php echo ($data['formula'] != $data['value']) ? ' *' : ''; ?>
        </li>
      <?php endforeach; ?>
    </div>
    <?php endforeach; ?>

  </body>
<html>

图 7 显示 清单 10 返回的数据。在图 7 中,从公式中生成了一个单元格值,该值使用星号突出显示。(查看 图 7 的文字版)。

图 7. 从单元格提要数组中检索到的工作表内容
从单元格提要数组中检索到的工作表内容的屏幕截图

过滤工作表数据

还可以过滤列表提要或单元格提要的内容,从而使提要只返回匹配特定条件的行或单元格。参见 清单 11,其中对列表提要进行了过滤,使其只返回销售额超过 25000 的条目。

清单 11. 过滤工作表列表提要
<!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>Filtering worksheet contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    div.sheet {
      margin: 10px;
      padding: 10px;
      border: silver 2px dotted;
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }    
    table, td {
      border: 1px solid black;
      vertical-align: top;
    }
    </style>    
  </head>
  <body>
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

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

    try {  
      // connect to API
      $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
      $service = new Zend_Gdata_Spreadsheets($client);

      // define worksheet query
      // get list feed for query
      $query = new Zend_Gdata_Spreadsheets_ListQuery();
      $query->setSpreadsheetKey('ssid');
      $query->setWorksheetId('wsid');
      $query->setSpreadsheetQuery('salesunits > 25000');
      $listFeed = $service->getListFeed($query);  
    } catch (Exception $e) {
      die('ERROR: ' . $e->getMessage());
    } 
    ?>
  
    <div class="sheet">
      <div class="name">Worksheet: 
        <?php echo $listFeed->getTitle(); ?></div>
      <table>
      <?php 
      foreach ($listFeed as $listEntry) {
        echo '<tr>';
        $rowData = $listEntry->getCustom();
        foreach($rowData as $customEntry) {
          echo '<td>' .  $customEntry->getText() . '</td>';
        }
        echo '</tr>';
      }
      ?>
      </table>
    </div>

  </body>
<html>

清单 11 初始化了一个 Zend_Gdata_Spreadsheets_ListQuery 对象,该对象可以以额外参数的方式传递给服务对象的 getListFeed() 方法,从而过滤列表提要中返回的数据。该对象必须使用三项输入进行初始化:

  • 电子表格 ID
  • 工作表 ID
  • 过滤条件

过滤结果是列表提要只包含满足过滤条件的工作表行。

过滤条件可以指定为列名,其后跟等式、不等式或布尔运算符,然后是过滤值。在向 Data API 服务器传递查询时,Zend_Gdata 自动对过滤条件进行 URL 编码。

图 8 演示了结果。

图 8. 按销售额过滤后的工作表内容
按销售额过滤后的工作表内容的屏幕截图

类似地,通过初始化 Zend_Gdata_Spreadsheets_CellQuery 对象并将其传递给 getCellFeed() 方法,可以对单元格提要中返回的数据进行限制。考虑 清单 12,其中演示了只检索指定工作表的标题行。

清单 12. 过滤工作表的单元格提要
<!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>Filtering worksheet contents</title>
    <style>
    body {
      font-family: Verdana;
    }
    div.sheet {
      margin: 10px;
      padding: 10px;
      border: silver 2px dotted;
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }
    table, td {
      border: 1px solid black;
      vertical-align: top;
    }
    </style>
  </head>
  <body>
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

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

    try {  
      // connect to API
      $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
      $service = new Zend_Gdata_Spreadsheets($client);

      // define worksheet query
      // get list feed for query
      $query = new Zend_Gdata_Spreadsheets_CellQuery();
      $query->setSpreadsheetKey('ssid');
      $query->setWorksheetId('wsid');
      $query->setMinRow(1);
      $query->setMaxRow(1);
      $cellFeed = $service->getCellFeed($query);
    } catch (Exception $e) {
      die('ERROR: ' . $e->getMessage());
    }
    ?>

    <div class="sheet">
      <div class="name">Worksheet: <?php echo $cellFeed->getTitle(); ?>
      </div>
      <table>
      <?php 
      echo '<tr>';
      foreach ($cellFeed as $cellEntry) {
        echo '<td>' .  $cellEntry->getCell()->getText() . '</td>';
      }
      echo '</tr>';
      ?>
      </table>
    </div>

  </body>
<html>

清单 12 初始化一个 Zend_Gdata_Spreadsheets_CellQuery,并使用查询对象的 setMinRow()setMaxRow()setMinColumn()setMaxColumn() 方法定义应当在提要中返回的一系列单元格。产生的提要仅包含指定范围的单元格。图 9 演示了结果。

图 9. 按行号过滤的工作表内容
按行号过滤的工作表内容的屏幕截图

示例应用程序:基于浏览器的电子表格查看器

现在您已经了解了从 Google Spreadsheets Data API 检索数据的基本知识,接下来看一个实际的例子。这个 next 应用程序结合使用 PHP 和 JavaScript 呈现一个数据网格,可以反应在线工作表的内容。它利用 jQuery 和 jqGrid(参见 参考资料 中的下载链接)生成数据网格,并使用 PHP 连接到 Google Spreadsheets,检索工作表内容,并将其格式化为一个可由 jqGrid 处理的 XML 文档。

清单 13 中的 PHP 代码用于连接 Google Spreadsheets Data API,执行一个查询以获取工作表的列表提要,并将获得的数据转换为 jqGrid 的 XML 格式。

清单 13. 检索和重新格式化工作表的列表提要
<?php
// get page number from request
$page = $_GET['page']; 
if (empty($page)) {
  $page = 1;
}

// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_Spreadsheets');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

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

// set records per page
$recordsPerPage = 5;
$startIndex = (($page-1) * $recordsPerPage)+1;

try {
  // connect to API
  $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;
  $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
  $service = new Zend_Gdata_Spreadsheets($client);

  // get spreadsheet entry
  $query = new Zend_Gdata_Spreadsheets_ListQuery();
  $query->setSpreadsheetKey('ssid');
  $query->setWorksheetId('wsid');
  $query->setStartIndex($startIndex);
  $query->setMaxResults($recordsPerPage);
  $listFeed = $service->getListFeed($query); 
  $totalRecords = (string)$listFeed->getTotalResults()+0; // cast to integer

  // send XML headers
  header("Content-type: text/xml"); 
  echo "<?xml version='1.0'?" . ">\n";

  // send summary information
  echo '<rows>'; 
  echo '<page>' . $page . '</page>';
  echo '<total>' . ceil($totalRecords/$recordsPerPage) . '</total>';
  echo '<records>' . $totalRecords . '</records>';

  // send records
  foreach ($listFeed as $listEntry) {
    echo '<row>';
    $rowData = $listEntry->getCustom();
    foreach($rowData as $customEntry) {
      echo '<cell>' .  $customEntry->getText() . '</cell>';
    }
    echo '</row>';
  }
  echo '</rows>';
} catch (Exception $e) {
  die('ERROR: ' . $e->getMessage());
}    
?>

注意,清单 13 实现了一个简单的分页系统,它预估请求 URL 中的页码。该页码然后用来计算将为当前页面返回的记录部分,而 setStartIndex()setMaxResults() 方法负责对列表提要进行相应地配置。

图 10 演示了 清单 13 返回的 XML 格式。(查看 图 10 的文字版。)

图 10. 清单 13 的 XML 输出
清单 13 的 XML 输出的屏幕截图

jqGrid 读取这种 XML 格式并将其转化为一个数据网格,使用了 清单 14 中的代码。

清单 14. 将电子表格显示为客户端数据网格
<html>
  <head>
    <link rel="stylesheet" type="text/css" media="screen"
      href="jquery-ui-1.8.6.custom.css" />
    <link rel="stylesheet" type="text/css" media="screen" href="ui.jqgrid.css" />
    <script src="jquery-1.4.2.min.js" type="text/javascript"></script>
    <script src="jquery.jqGrid.min.js" type="text/javascript"></script>  
    <script src="grid.locale-en.js" type="text/javascript"></script>
  </head>
  <body>
    <table id="list1">
    </table> 
    <div id="pager1">
    </div> 
    <script type="text/javascript">
    // code based on XML example at: http://www.trirand.com/blog/jqgrid/jqgrid.html
    jQuery().ready(function (){
      jQuery("#list1").jqGrid({
        url:'generate.php',
        datatype: "xml",
        colNames:
          ['Sector','Sales (Units)', 'Sales ($)'],
        colModel:
          [
            {name:'sector'},
            {name:'salesunits'},
            {name:'sales'},
          ],
        pager: jQuery('#pager1'),
        rowNum:5,
        autowidth: true,
        viewrecords: true,
      }).navGrid('#pager1',{edit:false,add:false,del:false})
      });
    </script>
  </body>
</html>

图 11 演示了结果输出(每个销售区在电子表格中按照销售量和销售额显示销售情况)。

图 11. 从 Google Spreadsheets 提要中生成的客户端数据网格
从 Google Spreadsheets 提要中生成的客户端数据网格的屏幕截图

结束语

正如本文给出的示例所示,Google Spreadsheets Data API 可以将来自 Google Spreadsheet 的数据轻松地直接集成到一个 PHP 应用程序。本文主要介绍从 Google Spreadsheets 中检索数据,可用的基本的提要类型,并解释了如何使用 Zend_Gdata 进行处理。本文还解释了如何检索用户的电子表格,检查每个电子表格以检索可用工作表的列表,以及使用列表和单元格提要访问电子表格的实际内容。最后,本文还介绍了数据过滤的基本知识,解释了如何显示匹配特定条件的或某个范围内的电子表格行或单元格。

Google Spreadsheets Data API 的功能远远不止于此。您还可以从一个 PHP 应用程序中查看电子表格数据,并添加、编辑、删除单元格、行和工作表。本文的第二部分将详细描述 Google Spreadsheets Data API 的这些方法,敬请关注!

参考资料

学习

获得产品和技术

  • Zend Framework:使用广泛可用的 API 下载和构建更安全、更可靠、更现代化的 Web 2.0 应用程序和 web 服务。
  • jQuery:获得 jQuery,一种快速、精简的 JavaScript 库,可以简化 HTML 文档遍历,事件处理和 Ajax 交互,加快 web 开发。
  • jqGrid:下载并试用这个支持 Ajax 的 JavaScript 控件,在 web 上表示和操作表格数据。
  • IBM 产品评估试用版软件:下载或 IBM SOA Sandbox for People,并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML, Open source
ArticleID=608367
ArticleTitle=使用 PHP 集成 Google Spreadsheets 数据,第 1 部分
publish-date=01172011