Skip to content

9. PHP 的网络功能

现在我们来探讨 PHP 的网络功能,这些功能使我们能够进行 TCP / IP(传输控制协议 / 互联网协议)的编程。

9.1. 获取互联网上某台机器的名称或地址(IP)(inet_01)


<?php

// 执行“机器名称 <--> 地址”功能 IP 机器
ini_set("display_errors", "off");
// 常量
$HOTES = array("istia.univ-angers.fr", "www.univ-angers.fr", "www.ibm.com", "localhost", "", "xx");
// $HOTES 机器的 IP 地址
for ($i = 0; $i < count($HOTES); $i++) {
  getIPandName($HOTES[$i]);
}
// 结束
exit;

//------------------------------------------------
function getIPandName($nomMachine) {
//$nomMachine:要获取其地址的机器名称 IP
// nomMachine-->地址 IP
  $ip = gethostbyname($nomMachine);
  if ($ip != $nomMachine) {
    print "ip[$nomMachine]=$ip\n";
// 地址 IP --> nomMachine
    $name = gethostbyaddr($ip);
    if ($name != $ip) {
      print "name[$ip]=$name\n";
    } else {
      print "Erreur, machine[$ip] non trouvée\n";
    }
  } else {
    print "Erreur, machine[$nomMachine] non trouvée\n";
  }
}

结果

ip[istia.univ-angers.fr]=193.49.146.171
name[193.49.146.171]=istia.istia.univ-angers.fr
ip[www.univ-angers.fr]=193.49.144.40
name[193.49.144.40]=ametys-fo.univ-angers.fr
ip[www.ibm.com]=129.42.56.216
Erreur, machine[129.42.56.216] non trouvée
ip[localhost]=127.0.0.1
name[127.0.0.1]=localhost127.0.0.1
ip[]=192.168.1.11
name[192.168.1.11]=st-PC.home
Erreur, machine[xx] non trouvée

注释

  • 第 4 行:要求不显示运行错误。

第 15 行中的 getIpandName 函数使用了 PHP 的网络函数。

  • 第 18 行:gethostbyname($nom) 函数用于获取名为 $nom 的机器的 IP "ip3.ip2.ip1.ip0" 地址。 如果机器 $nom 不存在,该函数将返回 $nom 作为结果。
  • 第 22 行:函数 gethostbyaddr($ip) 用于获取地址为 $ip 的机器名称,其格式为 "ip3.ip2.ip1.ip0"。 如果机器 $ip 不存在,该函数将返回 $ip 作为结果。

9.2. 一个 Web 客户端 ( inet_02)

一个用于获取网站首页内容的脚本。


<?php

// 错误处理
ini_set("display_errors","off");
// 获取 URL 的文本 HTML
// 网站列表
$SITES = array("istia.univ-angers.fr", "www.univ-angers.fr", "www.ibm.com", "xx");
// 读取表格中网站的索引页面 $SITES
for ($i = 0; $i < count($SITES); $i++) {
// 读取网站 $SITES 的索引页 [$i]
  $résultat = getIndex($SITES[$i]);
// 显示结果
  print "$résultat\n";
}//用于
// 结束
exit;

//-----------------------------------------------------------------------
function getIndex($site) {
// 读取 URL $site/ 并将其存储在文件 $site.html 中
// 创建文件 $site.html
  $html = fopen("$site.html", "w");
  if (!$html)
    return "Erreur lors de la création du fichier $site.html";

// 在 $site 的 80 端口上建立连接
  $connexion = fsockopen($site, 80);
// 若发生错误则返回
  if (!$connexion)
    return "Echec de la connexion au site ($site,80) : $erreur";
// $connexion 代表客户端(本程序)与所联系的Web服务器之间的双向通信流
// 在客户端(本程序)与所连接的Web服务器之间
// 该通道用于交换命令和信息
// 对话协议为 HTTP
// 客户端发送 get 命令以请求 URL /
// GET语法 URL HTTP/1.0
// HTTP 协议的头部(headers)必须以空行结尾
  fputs($connexion, "GET / HTTP/1.0\n\n");
// 服务器现在将在 $connexion 通道上进行响应。它将发送所有
// 这些数据,然后关闭通道。因此客户端会读取来自 $connexion
// 直到通道关闭
  while ($ligne = fgets($connexion, 1000))
    fputs($html, $ligne);
// 客户端随后关闭连接
  fclose($connexion);
// 关闭文件 $html
  fclose($html);
// 返回
  return "Transfert réussi de la page index du site $site";
}

结果:例如,网站 [www.ibm.com] 收到的文件:

HTTP/1.1 302 Found
Date: Wed, 08 Jun 2011 15:43:56 GMT
Server: IBM_HTTP_Server
Content-Type: text/html
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Pragma: no-cache
Cache-Control: no-cache, must-revalidate
Location: http://www.ibm.com/us/en/
Content-Length: 209
Kp-eeAlive: timeout=10, max=14
Connection: Keep-Alive

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="http://www.ibm.com/us/en/">here</a>.</p>
</body></html>
  • 第1-11行是服务器响应的HTTP头部信息
  • 第 1 行:服务器要求客户端重定向至第 8 行指定的 URL
  • 第2行:响应的日期和时间
  • 第3行:Web服务器的标识
  • 第4行:服务器发送的内容。此处为第13行开始的HTML页面
  • 第 12 行:结束 HTTP 头部的空行
  • 第13-19行:Web服务器发送的HTML页面。

代码注释:

  • 第 7 行:要获取其首页的网站 URL 列表。该列表将存储在文本文件 [nomsite.html] 中。
  • 第 11 行:函数 getIndex 负责执行此操作
  • 第19行:函数 getIndex($site) 下载网站 $site 的根页面(或首页),并将其存储在文本文件 $site.html 中。
  • 第27行:fsockopen($site,$port) 函数用于与 TCP 服务建立连接 / IP 服务,该服务运行在 $site 机器的 $port 端口上。 一旦客户端/服务器连接建立,许多 TCP / IP 服务便会交换文本行。此处的情况正是 HTTP 协议(HyperText 传输协议)的示例。 此时,从服务器发往客户端的数据流可被视为文本文件进行处理。从客户端发往服务器的数据流亦是如此。
  • 第38行:fputs函数允许客户端向服务器发送数据。此处发送的文本行含义如下:“我想要(GET)当前连接网站的根页面(/)。 我使用的是 HTTP 协议 1.0 版”。该协议的当前版本为 1.1。
  • 第 42 行:服务器响应中的文本行可以通过 while 循环逐行读取,并保存到文本文件 [$site.html] 中。当 Web 服务器发送了所请求的页面后,它会关闭与客户端的连接。 在客户端,这将被检测为文件结束。

9.3. SMTP 客户端 (inet_03)

在 TCP / IP 等协议中,SMTP(SendMail 传输协议)是消息发送服务的通信协议。

注:

  • 在安装了杀毒软件的Windows机器上,杀毒软件可能会阻止脚本PHP连接到SMTP服务器的25号端口。此时需要禁用杀毒软件。以McAfee为例,操作步骤如下:
  • 在 [1] 中,启用 VirusScan 控制台
  • 在 [2] 中,停止 [Protection lors de l'accès] 服务
  • 在 [3] 时,服务已停止

脚本:


<?php

// 客户端 SMTP(SendMail 传输协议)用于发送消息
// 信息取自一个名为 $INFOS 的文件,该文件包含以下行
// 第 1 行:smtp、发件人、收件人
// 后续行:消息正文
// 发件人:发件人邮箱
// 收件人:收件人邮箱
// smtp:要使用的 smtp 服务器名称
// 通信协议 SMTP 客户端-服务器
// -> 客户端连接到 SMTP 服务器的 25 号端口
// <- 服务器向其发送欢迎信息
// -> 客户端发送命令 EHLO:其主机名
// <- 服务器返回 OK 或不响应
// -> 客户端发送邮件指令:mail from: <发件人>
// <- 服务器响应 OK 或不响应
// -> 客户端发送 rcpt 命令至:<收件人>
// <- 服务器响应 OK 或不响应
// -> 客户端发送 data 命令
// <- 服务器响应 OK 或不响应
// -> 客户端发送消息中的所有行,并以包含
// 的单个字符。
// <- 服务器响应 OK 或不响应
// -> 客户端发送 quit 命令
// <- 服务器响应 OK 或不响应
// 服务器的响应格式为 xxx 文本,其中 xxx 是三位数。所有 xxx 数值 >=500
// 表示发生错误。响应可能包含多行,除最后一行外均以 xxx 开头
// 格式为 xxx(空格)
// 交换的文本行必须以字符 RC(#13) 和 LF(#10) 结尾

// 数据
$INFOS = "mail.txt"; // 邮件发送参数
// 获取邮件参数
list($erreur, $smtpServer, $expéditeur, $destinataire, $message) = getInfos($INFOS);
// 错误?
if ($erreur) {
  print "$erreur\n";
  exit;
}
print "Envoi du message [$smtpServer,$expéditeur,$destinataire]\n";
// 以详细模式发送邮件
$résultat = sendmail($smtpServer, $expéditeur, $destinataire, $message, 1);
print "Résultat de l'envoi : $résultat\n";
// 结束
exit;

//-----------------------------------------------------------------------
function getInfos($fichier) {
// 返回信息($smtp,$expéditeur,$destinataire,$message) 来自文本文件 $fichier
// 第 1 行:smtp、发件人、收件人
// 后续行:邮件正文

// 打开 $fichier
  $infos = fopen($fichier, "r");
// 文件 $fichier 是否存在
  if (!$infos)
    return array("Le fichier $fichier n'a pu être ouvert en lecture");
// 读取第一行
  $ligne = fgets($infos, 1000);
// 删除换行符
  $ligne = cutNewLineChar($ligne);
// 提取 smtp、发件人、收件人字段
  $champs = explode(",", $ligne);
// 字段数量是否正确?
  if (count($champs) != 3)
    return "La ligne 1 du fichier $fichier (serveur smtp, expéditeur, destinataire) a un
nombre de champs incorrect";
// 对提取的信息进行“处理”
  for ($i = 0; $i < count($champs); $i++)
    $champs[$i] = trim($champs[$i]);
// 提取字段
  list($smtpServer, $expéditeur, $destinataire) = $champs;
// 读取邮件
  $message = "";
  while ($ligne = fgets($infos, 1000))
    $message.=$ligne;
  fclose($infos);
// 返回
  return array("", $smtpServer, $expéditeur, $destinataire, $message);
}

//-----------------------------------------------------------------------

function sendmail($smtpServer, $expéditeur, $destinataire, $message, $verbose) {
// 将 $message 发送至 SMTP 服务器 $smtpserver,发件人为 $expéditeur
// 发给 $destinataire。若 $verbose=1,则跟踪客户端与服务器的通信
// 获取客户端名称
  $client = gethostbyaddr(gethostbyname(""));
// 在 $smtpServer 的 25 端口上建立连接
  $connexion = fsockopen($smtpServer, 25);
// 若发生错误则返回
  if (!$connexion)
    return "Echec de la connexion au site ($smtpServer,25)";
// $connexion 代表双向通信流
// 在客户端(本程序)与所连接的 SMTP 服务器之间
// 该通道用于交换命令和信息

// 连接建立后,服务器发送一条欢迎消息,我们读取该消息
  $erreur = sendCommand($connexion, "", $verbose, 1);
  if ($erreur) {
    fclose($connexion);
    return $erreur;
  }

// ehlo命令:
  $erreur = sendCommand($connexion, "EHLO $client", $verbose, 1);
  if ($erreur) {
    fclose($connexion);
    return $erreur;
  }

// mail from 命令:
  $erreur = sendCommand($connexion, "MAIL FROM: <$expéditeur>", $verbose, 1);
  if ($erreur) {
    fclose($connexion);
    return $erreur;
  }

// 命令 rcpt to:
  $erreur = sendCommand($connexion, "RCPT TO: <$destinataire>", $verbose, 1);
  if ($erreur) {
    fclose($connexion);
    return $erreur;
  }

// 数据命令
  $erreur = sendCommand($connexion, "DATA", $verbose, 1);
  if ($erreur) {
    fclose($connexion);
    return $erreur;
  }

// 准备发送消息
  // 它必须包含以下行
  // 发件人:发件人
  // 收件人:
  // 空行
  // 消息
  // .
  $data = "From: $expéditeur\r\nTo: $destinataire\r\n$message\r\n.\r\n";
  $erreur = sendCommand($connexion, $data, $verbose, 0);
  if ($erreur) {
    fclose($connexion);
    return $erreur;
  }

// 退出命令
  $erreur = sendCommand($connexion, "QUIT", $verbose, 1);
  if ($erreur) {
    fclose($connexion);
    return $erreur;
  }

// 结束
  fclose($connexion);
  return "Message envoyé";
}

// --------------------------------------------------------------------------

function sendCommand($connexion, $commande, $verbose, $withRCLF) {
// 将 $commande 发送到频道 $connexion
// 如果 $verbose=1,则启用详细模式
// 如果 $withRCLF=1,则将序列 RCLF 添加到交换中

// 数据
  if ($withRCLF)
    $RCLF = "\r\n"; else
    $RCLF="";

// 若 $commande 不为空,则发送命令
  if ($commande) {
    fputs($connexion, "$commande$RCLF");

// 可能的回显
    if ($verbose)
      affiche($commande, 1);
  }//if

// 读取响应
  $réponse = fgets($connexion, 1000);

// 可能的回显
  if ($verbose)
    affiche($réponse, 2);

// 获取错误代码
  $codeErreur = substr($réponse, 0, 3);

// 是否为响应的最后一行?
  while (substr($réponse, 3, 1) == "-") {

     // 读取响应
    $réponse = fgets($connexion, 1000);

     // 可能的回显
    if ($verbose)
      affiche($réponse, 2);
  }//while
// 响应结束

// 服务器返回的错误?
  if ($codeErreur >= 500)
    return substr($réponse, 4);

// 无错误返回
  return "";
}

// --------------------------------------------------------------------------

function affiche($échange, $sens) {
// 在屏幕上显示 $échange
// 如果 $sens=1,则显示 -->$echange
// 如果 $sens=2,则显示 <-- $échange(去除最后两个字符)RCLF
  switch ($sens) {
    case 1:
      print "--> [$échange]\n";
      return;
    case 2:
      $L = strlen($échange);
      print "<-- [" . substr($échange, 0, $L - 2) . "]\n";
      return;
  }//切换
}

// --------------------------------------------------------------------------

function cutNewLinechar($ligne) {
// 如果存在,则删除 $ligne 中的行尾标记
  ...
}

文件 infos.txt

1
2
3
4
5
6
7
smtp.orange.fr, serge.tahe@univ-angers.fr , serge.tahe@istia.univ-angers.fr
Subject: test

ligne1
ligne2

ligne3
  • 第1行:[smtp.orange.fr] 用于发送邮件的服务器,[serge.tahe@univ-angers.fr] 发件人地址,[serge.tahe@istia.univ-angers.fr] 收件人地址
  • 第2行:邮件头。此处仅有一项,即邮件主题。
  • 第3行:空行结束邮件头
  • 第4-7行:邮件正文

屏幕显示结果

Envoi du message [smtp.orange.fr,serge.tahe@univ-angers.fr,serge.tahe@univ-angers.fr]
<-- [220 mwinf5d05 ME ESMTP server ready]
--> [EHLO st-PC.home]
<-- [250-mwinf5d05 hello [2.1.22.82], pleased to meet you]
<-- [250-HELP]
<-- [250-AUTH LOGIN PLAIN]
<-- [250-SIZE 44000000]
<-- [250-ENHANCEDSTATUSCODES]
<-- [250-8BITMIME]
<-- [250 OK]
--> [MAIL FROM: <serge.tahe@univ-angers.fr>]
<-- [250 2.1.0 <serge.tahe@univ-angers.fr> sender ok]
--> [RCPT TO: <serge.tahe@univ-angers.fr>]
<-- [250 2.1.5 <serge.tahe@univ-angers.fr> recipient ok]
--> [DATA]
<-- [354 enter mail, end with "." on a line by itself]
--> [From: serge.tahe@univ-angers.fr
To: serge.tahe@univ-angers.fr
Subject: test

ligne1
ligne2
ligne3
.
]
<-- [250 2.0.0 Ag3i1h0051mFoG203g3ip1 mail accepted for delivery]
--> [QUIT]
<-- [221 2.0.0 mwinf5d05 ME closing connection]
Résultat de l'envoi : Message envoyé
  • 第 1 行:脚本执行跟踪信息
  • 第2行:SMTP服务器的首次响应。该响应发生在客户端连接到SMTP服务器25号端口之后。服务器的响应行格式为[xxx message]或[xxx-message]。 第一种语法表示响应已结束。xxx为结果代码。大于或等于500的值表示发生错误。第二种语法表示响应尚未结束,后续将有另一行内容。
  • 第 2 行:SMTP 服务器表示已准备好接收命令
  • 第 3 行:客户端发送命令 [EHLO nomDeMachine],其中 nomDeMachine 是运行该客户端的机器的互联网名称
  • 第 4-10 行:SMTP 服务器的响应
  • 第11行:客户端发送命令[MAIL FROM: <expéditeur>],该命令指定了发件人的电子邮件地址。
  • 第 12 行:SMTP 服务器表示接受该地址。如果语法不正确,它本可能会拒绝该地址。不过,它不会验证该电子邮件地址是否真实存在。
  • 第13行:客户端发送命令[RCPT TO: <destinataire>],该命令指定了邮件收件人的地址。
  • 第14行:SMTP服务器响应,表示接受该地址。此处同样进行了语法验证。
  • 第15行:客户端发送命令[DATA],告知用户后续行即为邮件正文。
  • 第16行:服务器响应称消息可以发送。该消息由多行文本组成,末尾必须为仅包含一个字符(句点)的行。
  • 第17-25行:客户端发送的消息
  • 第17-19行:消息头[From:, To:, Subject:]分别用于标识消息的发件人、收件人和主题。
  • 第 20 行:空行,表示头部结束
  • 第21-23行:消息正文
  • 第 24 行:仅由一个句点组成的行,表示消息结束。
  • 第 26 行:SMTP 服务器响应,表示接受该消息
  • 第 27 行:客户端发送命令 [QUIT] 以表示已完成
  • 第 28 行:SMTP 服务器回复称将关闭与客户端的连接

代码注释

由于脚本代码已附有大量注释,此处不再赘述。

  • 第 48-80 行:该函数处理文件 [infos.txt],该文件包含待发送的消息以及发送所需的必要信息。 该函数返回一个数组($erreur、$smtpServer、$expéditeur、$destinataire、$messge),其中:
    • $erreur:可能的错误消息,否则为空。
    • $smtpServer:需连接的 SMTP 服务器名称
    • $expéditeur:发件人的电子邮件地址
    • $destinataire:收件人的电子邮件地址
    • $message:待发送的消息。除消息正文外,还可能包含邮件头。
  • 第 84-157 行:函数 sendMail 负责发送邮件。其参数如下:
    • $smtpServer:需要连接的SMTP服务器名称
    • $expéditeur:发件人的电子邮件地址
    • $destinataire:收件人的电子邮件地址
    • $message:待发送的消息。
    • $verbose:值为 1 时,表示与 SMTP 服务器的通信记录需显示在控制台上。

函数 sendMail 返回一条错误消息,若未发生错误则返回空值。

  • 第 88 行:用于获取运行 Windows 的计算机的 Windows 名称。
  • 第 99 行:我们看到客户端/服务器对话遵循以下模式:
    • 客户端发送一行命令
    • 服务器在一行或多行中进行响应
  • 第161-208行:函数sendCommand支持以下参数:
    • $connexion:连接客户端与服务器的 TCP / IP 通道
    • $commande:要发送到该通道的命令。将读取服务器对此命令的响应。
    • $verbose:设为1表示需在控制台显示与SMTP服务器的通信内容。
    • $withRCLF:值为 1 时,表示需在命令末尾添加换行符“\r\n”
  • 第 173 行:客户端发送命令
  • 第 181 行:读取 SMTP 服务器响应的第一行,格式为 xxx 文本或 xxx-文本。后一种情况表示服务器还有另一行要发送。xxx 是服务器发送的错误代码。
  • 第 188 行 – 获取响应中的错误代码
  • 第 191-199 行:读取响应中的其余行
  • 第203-204行:若错误代码 >=500,则表示SMTP服务器报告了错误。

9.4. 第二个邮件发送程序(inet_04)

该脚本与前一个具有相同功能:发送邮件。为此,我们使用了PEAR库中的模块。该库包含数十个涵盖不同领域的模块。我们将使用以下模块:

  • Net/SMTP:用于与服务器进行交互的模块SMTP
  • Mail:一个支持通过不同协议管理邮件发送的模块。
  • Mail/Mime:一个用于创建可包含附件的邮件的模块。

要使用这些模块,首先需将其安装在运行脚本 PHP 的机器上。 安装软件包 WampServer 时,已安装了解释器 PHP。在其目录树中,可以安装模块 PEAR。

  • 在 [1] 中,PHP 解释器的安装文件夹
  • [2],文件夹 PEAR 将包含待安装的模块 PEAR
  • [3],用于初始化库 PEAR 的命令文件 [go-pear.bat]

要初始化库 PEAR,需打开窗口 DOS 并执行文件 [go-pear.bat]。该脚本将连接到库 PEAR 的网站。 因此需要互联网连接。

C:\serveursSGBD\wamp21\bin\PHP\php5.3.5>go-pear.bat

连接到图书馆网站 PEAR 后,脚本将下载若干内容。其中包括一个新脚本 [pear.bat]。我们将使用该脚本安装所需的各个模块 PEAR。 调用该脚本时需提供参数。其中,参数 [help] 可显示脚本支持的命令列表:

C:\serveursSGBD\wamp21\bin\PHP\php5.3.5>pear help
Commands:
build                  Build an Extension From C Source
bundle                 Unpacks a Pecl Package
channel-add            Add a Channel
channel-alias          Specify an alias to a channel name
channel-delete         Remove a Channel From the List
channel-discover       Initialize a Channel from its server
channel-info           Retrieve Information on a Channel
channel-login          Connects and authenticates to remote channel server
channel-logout         Logs out from the remote channel server
channel-update         Update an Existing Channel
clear-cache            Clear Web Services Cache
config-create          Create a Default configuration file
config-get             Show One Setting
config-help            Show Information About Setting
config-set             Change Setting
config-show            Show All Settings
convert                Convert a package.xml 1.0 to package.xml 2.0 format
cvsdiff                Run a "cvs diff" for all files in a package
cvstag                 Set CVS Release Tag
download               Download Package
download-all           Downloads each available package from the default channel

info                   Display information about a package
install                Install Package
list                   List Installed Packages In The Default Channel
list-all               List All Packages
list-channels          List Available Channels
list-files             List Files In Installed Package
list-upgrades          List Available Upgrades
login                  Connects and authenticates to remote server [Deprecated i
n favor of channel-login]
logout                 Logs out from the remote server [Deprecated in favor of c
hannel-logout]
makerpm                Builds an RPM spec file from a PEAR package
package                Build Package
package-dependencies   Show package dependencies
package-validate       Validate Package Consistency
pickle                 Build PECL Package
remote-info            Information About Remote Packages
remote-list            List Remote Packages
run-scripts            Run Post-Install Scripts bundled with a package
run-tests              Run Regression Tests
search                 Search remote package database
shell-test             Shell Script Test
sign                   Sign a package distribution file
svntag                 Set SVN Release Tag
uninstall              Un-install Package
update-channels        Update the Channel List
upgrade                Upgrade Package
upgrade-all            Upgrade All Packages [Deprecated in favor of calling upgr
ade with no parameters]
Usage: pear [options] command [command-options] <parameters>
Type "pear help options" to list all options.
Type "pear help shortcuts" to list all command shortcuts.
Type "pear help <command>" to get the help for the specified command.

命令 [install] 用于安装 PEAR 模块。可通过命令 [install] 获取帮助:

C:\serveursSGBD\wamp21\bin\PHP\php5.3.5>pear help install
pear install [options] [channel/]<package> ...
Installs one or more PEAR packages.  You can specify a package to install in four ways:

"Package-1.0.tgz" : installs from a local file

"http://example.com/Package-1.0.tgz" : installs from anywhere on the net.

"package.xml" : installs the package described in package.xml.  Useful for testing, or for wrapping a PEAR package in another package manager such as RPM.

"Package[-version/state][.tar]" : queries your default channel's server(pear.php.net) and downloads the newest package with the preferred quality/state (stable).

To retrieve Package version 1.1, use "Package-1.1," to retrieve Package state beta, use "Package-beta."  To retrieve an uncompressed file, append .tar (make sure there is no file by the same name first)

To download a package from another channel, prefix with the channel name like "channel/Package"

More than one package may be specified at once.  It is ok to mix these four ways of specifying packages.

Options:
  -f, --force
        will overwrite newer installed packages
  -l, --loose
        do not check for recommended dependency version
  -n, --nodeps
        ignore dependencies, install anyway
  -r, --register-only
        do not install files, only register the package as installed
  -s, --soft
        soft install, fail silently, or upgrade if already installed
  -B, --nobuild
        don't build C extensions
  -Z, --nocompress
        request uncompressed files when downloading
  -R DIR, --installroot=DIR
        root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM
  -P DIR, --packagingroot=DIR
        root directory used when packaging files, like RPM packaging
  --ignore-errors
        force install even if there were errors
  -a, --alldeps
        install all required and optional dependencies
  -o, --onlyreqdeps
        install all required dependencies
  -O, --offline
        do not attempt to download any urls or contact channels
  -p, --pretend
        Only list the packages that would be downloaded

需要安装的PEAR模块如下:[Mail]、[Mail_Mime]、[Net_SMTP]。在DOS窗口中,依次输入以下命令:

<PHP_installDir>pear install Mail
...
<PHP_installDir>pear install Mail_Mime
<PHP_installDir>pear install Net_SMTP

其中 <PHP_installDir> 是 PHP 解释器的安装目录 (本例中为 C:\serveursSGBD\wamp21\bin\PHP\php5.3.5)

可以看到已安装的模块:

C:\serveursSGBD\wamp21\bin\PHP\php5.3.5>pear list
INSTALLED PACKAGES, CHANNEL PEAR.php.NET:
=========================================
PACKAGE          VERSION STATE
Archive_Tar      1.3.7   stable
Console_Getopt   1.3.1   stable
Mail             1.2.0   stable
Mail_Mime        1.8.1   stable
Net_SMTP         1.6.0   stable
Net_Socket       1.0.10  stable
PEAR             1.9.4   stable
PHPUnit          1.3.2   stable
Structures_Graph 1.0.4   stable
XML_Util         1.2.1   stable

这些模块安装在 <PHP_installDir>/PEAR 文件夹中:

安装了PEAR模块后,发送邮件的脚本PHP变为如下内容:


<?php

// 错误处理
ini_set("display_errors", "off");
// 模块
ini_set("include_path", ".;C:\serveursSGBD\wamp21\bin\PHP\php5.3.5\PEAR");
require_once "Mail.php";
require_once "Mail/Mime.php";
require_once "Net/SMTP.php";


// 客户端 SMTP(SendMail 传输协议),用于发送消息
// 这些信息取自一个名为$INFOS的文件,其中包含以下行
// 第 1 行:smtp、发件人、收件人、附件
// 后续行:邮件正文
// 发件人:发件人邮箱
// 收件人:收件人邮箱
// smtp:要使用的 smtp 服务器名称
// 附件:要附加的文档名称
// 
// 数据
$INFOS = "mail2.txt"; // 邮件发送参数
// 获取邮件参数
list($erreur, $smtpServer, $expéditeur, $destinataire, $message, $sujet, $attachement) = getInfos($INFOS);
// 错误?
if ($erreur) {
  print "$erreur\n";
  exit;
}
print "Envoi du message [$smtpServer,$expéditeur,$destinataire,$sujet, $attachement]\n";
// 以详细模式发送邮件
$résultat = sendmail($smtpServer, $expéditeur, $destinataire, $message, $sujet, $attachement);
print "Résultat de l'envoi : $résultat\n";
// 结束
exit;

//-----------------------------------------------------------------------
function getInfos($fichier) {
// 返回信息 ($smtp,$expéditeur,$destinataire,$message, $sujet, $attachement) 取自文本文件 $fichier
// 第1行:smtp、发件人、收件人、主题、附件
// 后续行:邮件正文
...
// 回车
  return array("", $smtpServer, $expéditeur, $destinataire, $message, $sujet, $attachement);
}

//getInfos
//-----------------------------------------------------------------------

function sendmail($smtpServer, $expéditeur, $destinataire, $message, $sujet, $attachement) {
// 将 $message 发送至 SMTP 服务器 $smtpserver,发件人为 $expéditeur
// 发给 $destinataire。邮件中附带了文档 $attachement
// 该邮件的主题为 $sujet
// 
  // 消息
  $msg = new Mail_Mime();
  $msg->setTXTBody($message);
  $msg->addAttachment($attachement);
  $headers = $msg->headers(array("From" => $expéditeur, "To" => $destinataire, "Subject" => $sujet));
  // 发送
  $mailer = &Mail::factory("smtp", array("host" => $smtpServer, "port" => 25));
  $envoi = $mailer->send($expéditeur, $headers, $msg->get());
  if ($envoi === TRUE) {
    return "Message envoyé";
  } else {
    return $envoi;
  }
}

// --------------------------------------------------------------------------

function cutNewLinechar($ligne) {
// 如果存在,则删除 $ligne 的行尾标记
  
}

文件 [mail2.txt]:

1
2
3
4
smtp.orange.fr, serge.tahe@univ-angers.fr , serge.tahe@univ-angers.fr, test, document.pdf
ligne1
ligne2
ligne3
  • 第 1 行:依次为 SMTP 服务器、发件人地址、收件人地址、邮件主题、待附加的文档。
  • 第2-4行:邮件正文

屏幕显示结果

Envoi du message [smtp.orange.fr,serge.tahe@univ-angers.fr,serge.tahe@univ-angers.fr,test, document.pdf]
Résultat de l'envoi : Message envoyé

注释

该脚本与前一个脚本的唯一区别在于其 sendmail 函数。我们对此进行说明:

  • 第 6 行:模块 PEAR 中的脚本 PHP 被安装在一个默认情况下不会被解释器 PHP 搜索的目录中。 为了使解释器能够找到 PEAR 模块,我们需要手动指定 PHP 解释器应搜索的文件夹。在运行时,可以修改 PHP 解释器的某些配置参数。 此修改仅对执行该修改的脚本可见,且仅在脚本执行期间有效。PHP解释器的默认配置位于文件<PHP_installdir>/PHP.ini中。该文件包含以下形式的行
clé=valeur

该键的值可在运行时通过函数 ini_set(key, nouvelle_valeur) 进行修改。 用于指定解释器搜索脚本所引用的 PHP 函数和类的路径的键是 'include_path'。 在此,我们将脚本所在的文件夹(.)以及 PHP 解释器安装目录下的 PEAR 文件夹同时添加到搜索路径中。

  • 第7-9行:加载用于发送邮件的脚本PHP。
  • 第 50 行:负责发送邮件的 sendmail 函数。其参数如下:
    • $smtpServer:需要连接的SMTP服务器名称
    • $expéditeur:发件人的电子邮件地址
    • $destinataire:收件人的电子邮件地址
    • $message:待发送的邮件正文。
    • $sujet:邮件主题
    • $attachement:要作为邮件附件的文档名称

函数 sendMail 返回一条错误消息,若未发生错误则返回空值。

  • 第 56 行:创建类型为 Mail_Mime 的消息。该消息由标题(From、To、Subject)和正文(消息本身)组成
  • 第 57 行:设置 Mail_Mime 消息的正文。
  • 第 58 行:将文档附加到消息中
  • 第 59 行:设置消息 Mail_Mime 的头部(From、To、Subject)
  • 第 61 行:创建负责发送消息 Mail_Mime 的类该方法的第一个参数是要使用的协议名称,此处为 SMTP 协议。第二个参数是一个数组,用于指定要使用的 SMTP 服务名称和端口
  • 第 62 行:发送邮件。send 方法接收三个参数:发件人地址、邮件头 Mail_Mime 以及邮件正文 Mail_Mime。 如果发送成功,send 函数返回布尔值 TRUE;否则返回错误信息。