使用 XML 和 PHP 创建一个更具适应性的电话簿和通讯录

使用 XML 为您的电话提供一个一致的通讯录

经常出差的专业人士需要触手可及的任何电话上都有一个一致可靠的通讯录。本文以一个向桌面 SIP 电话和智能电话提供数据的 MySQL 数据库为例,展示如何使用 PHP 从同一个源数据库输出自定义 XML,满足桌面电话或智能电话的需求,或者同时满足二者的需求。

Colin Beckingham, 研究人员, 自由职业者

Colin Beckingham 居住在加拿大安大略省,是一位自由研究人员、作家和程序员。他拥有金斯顿皇后大学的学位,对园艺、赛马、教育、公共服务、零售和旅游/观光领域都有涉猎。他是数据库应用程序的作者,也是大量报纸、杂志和在线文章的撰稿人,他的研究兴趣包括 Linux ® 上的开源编程以及语音控制应用程序。



2011 年 5 月 09 日

您需要与联系人随时保持联系 — 即使在出差途中。在办公室,可以使用您的桌面 VoIP 电话;出差途中,可以使用智能电话。但每台设备都要求其电话列表采用一种特殊格式,每台设备都有自己的、格式与众不同的通讯录或电话簿。如果有必要,可能需要输入您的所有联系人两次 — 每个通讯录一次 — 每次变更联系人时,都需要同时在两个列表中进行更改。毫无疑问,您肯定希望这两部电话从同一个后端数据源获取数据,这样您就只需在一个地方编辑姓名和电话号码。这不成问题:只要能访问互联网或内联网。在本文中,了解如何使用一个 MySQL 数据库中的数据向两个完全不同的设备提供联系信息:一部通过 snom 300 系列设备标识的 VoIP 电话和一个采用 Nokia E71 设备形状的智能电话(参见 参考资料 中的链接)。

桌面设备:snom 300 系列

常用缩略词

  • HTML:超文本标记语言
  • HTTP:超文本传输协议
  • LAN:局域网
  • LDAP:轻量级目录访问协议
  • SQL:结构化查询语言
  • VoIP:IP 语音传输
  • XML:可扩展标记语言

snom 300 系列提供优质稳定的 VoIP 电话,这些电话拥有广泛的办公功能,比如等待、转接和电话会议。这类电话的确拥有对来自 LDAP 服务器的数据提要的原生支持,它还拥有一个能阅读 HTML 的微型浏览器(参见 参考资料)。它甚至能以一种能够触发电话呼叫的格式在电话屏幕上显示文本。

清单 1 展示 snom 微型浏览器需要(特别是在电话簿上下文中)的 XML 格式的示例。

清单 1. snom XML 微型浏览器示例
<?xml version="1.0" encoding="UTF-8"?>
<SnomIPPhoneDirectory>
  <Title>PhoneList - Snom</Title>
  <DirectoryEntry>
    <Name>Friend, First</Name>
    <Telephone>555-456-7890</Telephone>
  </DirectoryEntry>
  <DirectoryEntry>
    <Name>Person, Second</Name>
    <Telephone>555-654-0987</Telephone>
  </DirectoryEntry>
  <SoftKeyItem>
    <Name>F1</Name>
    <Label>Dial</Label>
    <SoftKey>F_ENTER</SoftKey>
  </SoftKeyItem>
</SnomIPPhoneDirectory>

在这段代码中,根元素 SnomIPPhoneDirectory 拥有 3 个不同子元素:TitleDirectoryEntrySoftKeyItem。标题位于电话显示的顶部,滚动过程中不间断显示。标题下方是目录条目 — 每行一个条目 — 且能够滚动。这些软键项目关联到显示下方的 4 个按钮。这些能执行一些功能,比如对当前突出显示的目录条目发起一个呼叫。这个示例中的数据包含两个电话号码条目:一个显示姓名和一个用于拨号的号码。它只激活一个按钮:按下 F1 按钮,电话就开始拨号。


旅行设备:Nokia E71 智能电话

Nokia E71 设备是一个典型现代智能电话示例。它能够连接无线 WAN 或移动电话服务,拥有一个 Session Initiation Protocol (SIP) 客户端,能使用自己的本机浏览器浏览 Internet,并以移动方式管理语音通信。

E71 设备支持 Wireless Application Protocol (WAP) 版本 2.0(WAP2 — 参见 参考资料)— 不要与 Wi-Fi Protected Access II (WPA2) 功能混淆。这意味着它能阅读 XML 格式文件,这类文件在电话内部与各种专业功能通信,比如打开一个窗口来发起一次呼叫。

清单 2 展示了这种智能电话需要的 WAP2/XML 格式内容示例。这个示例使用 清单 1 中显示的那两个虚构条目。

清单 2. WAP2 XML 示例
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
   "http://www.wapforum.org/DTD/wml_1.1.xml" >
<wml>
  <card id="main" title="PhoneList - Nokia">
    Tel (WTAI): <a href="wtai://wp/mc;%2B555-456-7890">Friend, First</a><br />
    Tel (WTAI): <a href="wtai://wp/mc;%2B555-654-0987">Person, Second</a>
  </card>
</wml>

WAP2 要求以卡片组的形式显示数据。根元素 <wml> 拥有一个子元素 <card>(或页面),该子元素有两行,通过一个换行符分隔。这个页面的标题在这个根元素的一个属性中列示。这些电话号码在一些锚点元素(即 <a>)中列示,有一个 <href> 属性使用 Wireless Telephony Applications Interface(WTAI — 参见 参考资料)。当您打开电话的浏览器中的页面并单击一个电话链接时,一个窗口将弹出并询问您是否要呼叫这个号码,这时电话找到一条路径来路由呼叫 — 无论是通过一条无线 LAN 还是使用另一个电话订阅。

由于智能电话没有处理 LDAP 的原生能力且桌面电话不能处理 WAP2,它们似乎没有什么共同点。但是,编写脚本通过使用 XML 的适应性提供了一种解决方案。您可能会找到一种方法,通过智能电话同步您的联系人和 LDAP 服务器,但这种方法可能不如直接访问信息那么简捷。幸运的是,尽管 XML 的格式在这两种架构之间可能会更改,但数据在根本上是相同的。


解决方案

鉴于这两种设备均可随时随地访问 Internet,实现一个通用电话簿的解决方案是将所有数据放置在一个数据库中,然后使用一个脚本引擎生成一个 XML 文件,并将其传递给您认为最方便的设备浏览器。另外,这个脚本能根据需要控制访问权并过滤要交付的信息。

数据库

数据可以存储为各种格式并根据需要提取,包括 PostgreSQL、XML、纯文本、IBM® DB2® 等。清单 3 展示了一个 MySQL 样例架构。

清单 3. 示例数据库架构
CREATE TABLE IF NOT EXISTS 'mycontacts' (
  'id' int(11) NOT NULL auto_increment,
  'firstName' varchar(30) default NULL,
  'lastName' varchar(30) default NULL,
  'number' varchar(20) default NULL,
  PRIMARY KEY  ('id')
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 ;

这个表只包含 4 个字段 — 一个 ID、姓名、电话号码 — 以及一个索引。该架构显示了所需要信息的关键组成部分。可以根据需要进一步添加字段和索引。可以根据需要采用您选择的 LibreOffice Base 或 phpMyEdit(参见 参考资料)编辑器编辑数据内容。

PHP 生成器

清单 4 展示了一个从后端获取数据并以纯文本格式显示输出的基本脚本。此时,您只是测试脚本,确保它返回合理的数据。

清单 4. 基本数据库生成器
<?php
    $dev = "";
    $db_host = "your.database.server";
    $db_user = "your_user";
    $db_pass = "your_password";
    $db_name = "your_database";
    $mysqli = new mysqli($db_host, $db_user, $db_pass, $db_name);
    if (mysqli_connect_errno()) show_err($dev,"Could not connect to database");
    $query = "SELECT * FROM mycontacts order by lastName asc";
    $result = $mysqli->query($query);
    $num = $result->num_rows;
    $i = 0;
    while ($row = $result->fetch_array()) {   
        $myarr[$i]['first']=$row["firstName"];
        $myarr[$i]['last']=$row["lastName"];
        $myarr[$i]['phone']=$row["number"];
        $i++;
    }
    $mysqli->close();
    switch ($dev) {
      case 'snom':
        echo mysnom($myarr);
      break;
      case 'noki':
        echo mynoki($myarr);
      break;
      default:
        echo mytest($myarr);
      break;
    }
function mytest($myarr) {
  $cont = "Header\n";
  foreach ($myarr as $a) {
    $cont .= " ".$a['first']." ".$a['last']." ".$a['phone']."\n";
  }
  $cont .= "Footer\n";
  return $cont;
}
function show_err($dev,$msg) {
  die($msg);
}
?>

这段代码首先定义用于访问数据库的变量,打开一个连接,返回一个结果集或一条错误消息(如果不能连接到数据库的话)。然后,一个 while 循环迭代数据集,在一个方便的数组中存储信息以便将来进行显示。由于设备变量 $dev 被初始化为一个长度为零的字符串,因此,当 switch 执行时,它将切换到默认值并调用 mytest() 函数。这个函数将显示一个简单的标题,写出数组内容,然后打印一个页脚 — 全部采用纯文本格式。

这个脚本只处理一个可能的错误:数据库连接故障。您应计划处理其他情况,比如成功连接到数据库但发现一个空表。您可以使用 show_err() 函数调用之类的调用捕获并处理这些情况。

大多数组件都已就绪,可以泛化这个脚本,处理其他设备类型。switch 拥有针对这两种电话的情况,但它们的功能还不存在。现在的问题是如何使输出与其他目标设备相关,根据需要添加 XML 格式的细节。

桌面微型浏览器

对照 清单 1 中的示例,您将看到,清单 5 中的函数添加了必要的组件来处理桌面 VoIP 电话。

清单 5. 处理桌面微型浏览器的函数
function mysnom($myarr) {
    $cont = "<?xml version=\"1.0\"?>
<SnomIPPhoneDirectory>
  <Title>MySQL Directory</Title>";
  foreach ($myarr as $a) {
    $cont .= "
  <DirectoryEntry>
    <Name>".$a['first']." ".$a['last']."</Name>
    <Telephone>".$a['phone']."</Telephone>
  </DirectoryEntry>\n";
  }
  $cont .= "</SnomIPPhoneDirectory>\n";
  return $cont;
}
function show_err($dev,$msg) {
  switch ($dev) {
    case 'snom':
      echo "<?xml version=\"1.0\"?>
<SnomIPPhoneText>
  <Text>
$msg
  </Text>
</SnomIPPhoneText>
";
    break;
    default:
      echo $msg;
    break;
  }
  die();
}

对比 清单 5清单 4,您会发现,这些新函数并不回显纯文本,而是回显微型浏览器预期的元素中封装的数据。mysnom() 函数是脚本的一个新增函数,show_err() 函数是一个替代函数。 微型浏览器不能显示纯文本:它什么也不做。因此,常规和异常输出都需要 XML 输出。由于您正在使用电话,因此电话也是错误消息必须显示的地方。如果出现数据库连接故障,脚本将向电话报告错误。如果成功连接,则在 while 循环开始之前,脚本应声明根元素。循环进行时,每条记录都被封装到记录自己的目录条目标记中;循环完成后,按钮的设置就开始了。由于只有 4 个按钮,是否也需要在数据库中对其进行编码还存在疑问。最后,代码结束了根元素。

上述函数要正常工作,必须在代码开头按如下方式正确设置变量 $dev

$dev = "snom";

智能电话 WAP2

如果是 Nokia 电话,则需要提供一些输出细节,它们通过 WTAI 引用以 WAP2 格式向智能电话提供 XML。清单 6 显示了相关代码。

清单 6. 处理智能电话 WAP2 的函数
function mynoki($myarr) {
  $cont = "<?xml version=\"1.0\"?>
<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"
   \"http://www.wapforum.org/DTD/wml_1.1.xml\" >
<wml>\n
  <card id=\"main\" title=\"PhoneList - Nokia\">\n";
  foreach ($myarr as $a) {
    $cont .= "\nTel (WTAI): <a href=\"wtai://wp/mc;%2B".$a['phone']."\">
      ".$a['last']." ".$a['first']."</a><br />";
  }
  $cont .= "</card></wml>\n";
  return $cont;
}

同样,这段代码向 Nokia 电话提供必要的 XML。如果出现数据库连接错误,文本输出不需要封装到任何标记中,因为简单输出无需任何标记即可在此电话上正确显示。当电话簿条目被显示时,在循环开始之前,脚本发送 XML 和 DOCTYPE 信息,然后是 <wml> 根元素和 <card> 元素的开始标记。然后,循环开始,显示封装在 WTAI 信息中的每条记录。最后,代码结束 card 元素和 <wml> 根元素。

上述函数要正常工作,必须在代码开头按如下方式正确设置变量 $dev

$dev = "noki";

请注意,Nokia E71 通讯录的解决方案建议并不打算完全替代原有联系人应用程序,后者作为电话系统的一部分,肯定有一定优点,因此能与其他应用程序形成紧密的内部连接。但是,原有通讯录可以使用同一流程从同一 MySQL 数据库生成,但生成 vCard (VCF) 文件格式(参见 参考资料)的中间输出,这样便于导入电话。


检测设备

这个问题的最后部分是让脚本在运行时了解需要哪种设备输出。可以通过几种方法完成此任务,其中一种方法是在 HTTP 请求查询字符串中发送 GET 信息。假设您发送以下请求,显式声明一个设备和一个用户:

http://www.myserver.tld/phonebook/myscript.php?device=snom&user=jim

然后,清单 7 中的最终脚本解析查询字符串并在运行时将信息插入脚本。

清单 7. 检测设备
<?php
    if ($_GET['user'] != 'jim') show_err($dev,'Unauthorised access');
    $dev = $_GET['device'];
    $db_host = "your.database.server";
    $db_user = "your_user";
    $db_pass = "your_password";
    $db_name = "your_database";
    $mysqli = new mysqli($db_host, $db_user, $db_pass, $db_name);
    if (mysqli_connect_errno()) show_err($dev,"Could not connect to database");
    $query = "SELECT * FROM mycontacts order by lastName asc";
    $result = $mysqli->query($query);
    $num = $result->num_rows;
    $i = 0;
    while ($row = $result->fetch_array()) {   
        $myarr[$i]['first']=$row["firstName"];
        $myarr[$i]['last']=$row["lastName"];
        $myarr[$i]['phone']=$row["number"];
        $i++;
    }
    $mysqli->close();
    switch ($dev) {
      case 'snom':
        echo mysnom($myarr);
      break;
      case 'noki':
        echo mynoki($myarr);
      break;
      default:
        echo mytest($myarr);
      break;
    }
function mytest($myarr) {
  $cont = "Header\n";
  foreach ($myarr as $a) {
    $cont .= " ".$a['first']." ".$a['last']." ".$a['phone']."\n";
  }
  $cont .= "Footer\n";
  return $cont;
}
function mynoki($myarr) {
  $cont = "<?xml version=\"1.0\"?>
<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"
   \"http://www.wapforum.org/DTD/wml_1.1.xml\" >
<wml>\n
  <card id=\"main\" title=\"PhoneList - Nokia\">\n";
  foreach ($myarr as $a) {
    $cont .= "\nTel (WTAI): <a href=\"wtai://wp/mc;%2B".$a['phone']."\">
      ".$a['last']." ".$a['first']."</a><br />";
  }
    $cont .= "</card></wml>\n";
  return $cont;
}
function mysnom($myarr) {
    $cont = "<?xml version=\"1.0\"?>
<SnomIPPhoneDirectory>
  <Title>MySQL Directory</Title>";
  foreach ($myarr as $a) {
    $cont .= "
  <DirectoryEntry>
    <Name>".$a['first']." ".$a['last']."</Name>
    <Telephone>".$a['phone']."</Telephone>
  </DirectoryEntry>\n";
  }
  $cont .= "</SnomIPPhoneDirectory>\n";
  return $cont;
}
function show_err($dev,$msg) {
  switch ($dev) {
    case 'snom':
      echo "<?xml version=\"1.0\"?>
<SnomIPPhoneText>
  <Text>
$msg
  </Text>
</SnomIPPhoneText>
";
    break;
    default:
      echo $msg;
    break;
  }
  die();
}
?>

这段代码基本上是 清单 4 的重复,只是添加了 清单 5清单 6 中的函数。代码首先进行一个简单检查,了解用户是谁,如果错误的用户企图访问信息,代码将在控制条件下停止。然后,代码从查询字符串获取设备,并根据指示完成输出。

由于每部电话都向数据库显示自己的请求和查询字符串,因此它们以设备可显示的格式轻松快速地接收输出。


结束语

您可以通过修改 XML 输出在其他平台上使用来自一个公共源的通讯录。针对 Nokia 的函数应该适用于任何遵守 WAP 2.0 的电话。针对其他电话改编 snom 函数可能更加复杂,但如果使用 XML,那么您需要的只是正确的架构。您可以使用任何方便的脚本语言、根据需要轻松维护和扩展这个脚本。

参考资料

学习

获得产品和技术

  • LibreOffice:了解这个面向 Windows、Macintosh 和 Linux 的 Open Source 个人生产力套件。试用下面 6 个用于文档生产和数据处理的应用程序:Writer、Calc、Impress、Draw、Math 和 Base。
  • phpMyEdit:使用这个工具编写一个简单的调用程序,生成一些 PHP 代码,以 HTML 格式显示或编辑的 MySQL 表。
  • IBM 产品评估版:下载或 在线试用 IBM SOA Sandbox,并开始使用来自 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=657325
ArticleTitle=使用 XML 和 PHP 创建一个更具适应性的电话簿和通讯录
publish-date=05092011