编写本章之前,笔者在考虑一个问题,那就是“如何把一本书的内容压缩到短短的一章中”。这确实是一个难题!但是,虽然篇幅有限,本书还是会尽量使用最简洁明了的语言和最易于理解的实例,来帮助大家以最快的速度认识和了解PHP的开发。
PHP(Hypertext Preprocessor)是目前最流行的服务端脚本语言之一。近年来,随着互联网的飞速发展,使用PHP语言进行互联网应用开发也变得逐渐火热起来,其特点是简单、快速、灵活,主要应用于各大门户网站、主流CMS平台以及Web 2.0网络应用中,包括Google、Yahoo、Facebook、Zynga在内的互联网巨头们也都大规模地使用PHP作为其主要的编程语言。那么,PHP究竟能用来做什么呢?一般来说,PHP在实际项目的应用过程中有以下两种主要的使用方式。
用于后台脚本编程,即以命令行(CLI)的方式执行由于PHP的语法和Linux Shell语言有点类似,而使用起来却要比Shell强大且方便得多,所以我们经常使用PHP作为后台可执行脚本的解决方案。这种方式下的PHP脚本,我们也常称之为CLI(Command-Line Interface)脚本。用于网络应用编程,即以mod_php或fastCGI的方式执行简单说就是用于开发网站或者互联网应用,这也是PHP最主要的使用方式。在这种方式中,PHP经常和其他的一些服务端组件结合使用,比如在著名的LAMP架构里,PHP就是与Apache服务器、MySQL数据库组成了互联网应用服务端开发的铁三角。PHP的这种使用方式通常被称为网络(Web)脚本模式。小贴士:LAMP即Linux、Apache、MySQL以及PHP/Perl/Python相结合的服务端解决方案,也是目前最强大的互联网应用解决方案之一,占据了全球的网站70%以上的市场。简单易用、性能强劲、完全免费这三个特点是LAMP为何如此受欢迎的原因之一。
了解过PHP语言的用途,接下来我们来看看如何使用PHP。首先,来学习一下PHP基本语法中的重点部分,以下就是“精简版”的PHP语法总结。当然,如果读者已经有过一些其他主流语言的编程经验,比如Java、C++等,那么笔者建议学习时,可以将PHP的语法与这些已经比较熟悉的语言进行对比学习,这样会事半功倍。
规范PHP代码部分需要用“<?php … ?>”符号框起来,这也表明你可以把PHP代码块嵌入到HTML代码的任何位置,这种用法类似ASP或者JSP。注释PHP中单行注释以“//”或者“#”符号开始,多行注释使用“/ … /”符号框起来,这点综合了Perl、C++以及Java语言的用法。变量PHP的所有变量都以“$”符号开始,变量的命名规则与C++和Java语言的标准基本相同,例如:$_user是正确的,$@user就是错误的。另外,由于PHP是解释性语言,具有弱类型性,所以PHP的变量不需要声明类型,这点与Java和 C++这些编译型的强类型语言是不同的。4.常量PHP使用define函数来定义常量,这点类似于C和C++语言。常量名我们一般都会使用全大写的字母,比如“define('CONSTANT', $constant);”这行代码就定义了一个值为$constant的CONSTANT常量。
函数自定义PHP的函数必须包含function关键字,比如“function hello () {...}”。此外,PHP语言的自带函数库是非常强大的,这点大家可以在日后使用中慢慢体会。类定义定义PHP类的方法和Java基本一致,比如“public class User {...}”。另外,在PHP 5发布后,PHP的面向对象功能越加强大,具体可参考本章3.1.4节的内容。允许文件中包含文件在PHP中允许包含其他的PHP文件,这样方便了我们进行代码的封装,一般来说使用require和include方法来包含。如果要避免重复包含的问题,则可以使用require_once和include_once方法。命名空间对于大型的项目来说,命名空间(Namespace)的功能还是非常必要,使用命名空间可以减少因为类名或者函数名相同所带来的风险。在PHP的新版本中(PHP 5.3),已经支持namespace语法,比如“namespace CoreLib1”。事实上,PHP的语法源自Perl语言,并融合了Java和C语言的部分优点,对于有一定编程基础的开发者来说上手非常快。首先,我们来观察一个PHP的Hello World程序,如代码清单3-1所示。代码清单 3-1
<?php // 打印字符串 echo "Hello World"; ?>从这段代码中我们可以看到一个标准PHP脚本的写法、打印字符串的方法echo,以及单行注释的写法。小贴士:在实际开发时,我们经常把PHP文件最后的“?>”符号去掉,因为这样写不仅不会影响PHP的语法解释,还可以避免一些由于编辑器在文件的末尾处自动加上特殊字符,从而影响PHP解释和输出的问题。接下来,我们来分析代码清单3-2中的PHP的程序范例。代码逻辑非常简单,最前面定义了一个名为“USERNAME”的常量,接着定义了一个函数isJames()用于判断输入的参数是否等于“James”,最后打印函数的测试结果。很显然这段代码的运行结果是false,因为传入值和比较值的大小写是不一样的。代码清单 3-2
<?php // 常量定义 define('USERNAME', "James"); // 函数定义 function isJames ($username) { if (USERNAME == $username) { return true; } return false; } // 打印结果 var_dump(isJames("james")); ?>以上代码包含了PHP语言中的注释、变量、常量以及函数等重要语法的使用方法,大家可以尝试在本地运行该脚本。运行方法很简单,直接使用php可执行文件执行即可,比如该php文件名为demo1.php,用户直接在系统命令行窗口中输入“php demo1.php”并运行即可。当然,在此之前我们还必须把php可执行文件的路径加入到系统环境变量中去,否则系统可能提示找不到php命令。代码清单3-2的运行结果如图3-1所示。
通过3.1.2节的学习,我们大致了解了PHP的基本语法,本节我们将进一步认识PHP语言。首先,我们来看看PHP语言的几个特色,也就是它和其他语言不大一样的地方。
预定义变量PHP提供大量的预定义变量,准确来说应该是预定义“数组”变量,用于存储来自服务器、运行环境和输入数据等动态信息,不同于其他语言使用对应的使用类包或者方法来获取的方式,相对来说PHP的这种方式更加简单直接。表3-1中列出了PHP语言中比较重要的预定义变量。以上这些预定义变量都是我们在开发过程中经常使用到的,但是需要注意的是它们中某些变量的使用环境是有限制的,比如$_GET、$_POST以及$_SERVER变量在CLI模式下是没有作用的,因为CLI不运行在服务器环境里。关于这点我们已经在上表的“环境”一列中说明了,其中标注Web的表示只有在网络脚本模式下才能使用;CLI表示只工作在CLI脚本模式下;其他的预定义变量在两种模式下均可使用,但是数组的内容可能会不同。以下是一个使用预定义变量的例子,不像Java还需要使用request.getParameter()方法逐个获取GET参数,PHP会直接把所有的GET参数全部放到预定变量$_GET中,我们可以直接循环打印出来,如代码清单3-3所示。
代码清单 3-3
<?php $sp = "<br/>\n"; foreach ((array) $_GET as $k => $v) { echo "GET $k : $v".$sp; } ?>由于这个脚本必须在Web模式下才能使用,因此我们需要把以上代码放入站点目录下,开启浏览器,运行结果如图3-2所示。
null、false、0和空字符串之间的关系在PHP中,如果一个变量没有赋值则为null,这点和Java类似;但是,PHP是一门“弱类型”的语言,因此对于PHP来说,null和false、0以及空字符串之间的关系有点特殊,如代码清单3-4所示。代码清单 3-4
<?php // null and 0 echo "null==0 : "; echo var_dump(null==0); // null and '' echo "null=='' : "; echo var_dump(null==''); // null and false echo "null==false : "; echo var_dump(null==false); // null and 0 echo "null===0 : "; echo var_dump(null===0); // null and '' echo "null==='' : "; echo var_dump(null===''); // null and false echo "null===false : "; echo var_dump(null===false); ?>以上脚本的运行结果如图3-3所示。我们来分析一下结果:首先,前3段代码说明了在PHP中null、false、0和空字符串之间是可以画等号的,这是因为它们在PHP都属于一种“非”类型,其他的“非”类型还包括空数组等;其次,看后3段代码,我们又可以发现,如果我们要区别这几个值也是可以做到的,在PHP中我们使用全等号便可以判别出它们之间的不同。我们在日常编码时一定要注意PHP的这个特性,如果随意乱用,很有可能会犯一些低级错误。
表3-2列出了PHP语言中对于“非”类型的汇总信息,大家在使用PHP的过程中一定要特别注意这些数值的使用方法。
魔术变量和魔术方法最初PHP魔术变量的出现主要是为了方便开发者调试PHP的代码;当然,我们也可以利用这些魔术变量来实现特殊需求。在写法方面,魔术变量前后都有两个下划线,接下来让我们来熟悉一下这些变量。__LINE__:返回文件中的当前行号,我们在定位错误的时候经常用到。 __FILE__:返回当前文件的完整路径和文件名。自PHP 4.0.2起,__FILE__总是包含一个绝对路径(如果是符号连接,则是解析后的绝对路径)。__DIR__:返回当前文件所在的目录(PHP 5.3.0中新增)。如果用在被包括文件中,则返回被包括的文件所在的目录,等价于dirname(__FILE__)。除非是根目录,否则目录中名称不包括末尾的斜杠。__FUNCTION__:返回当前函数的名称(PHP 4.3.0中新增)。自PHP 5起本常量返回该函数被定义时的名字,大小写敏感。在PHP 4中该值总是小写字母的。__CLASS__:返回当前类的名称(PHP 4.3.0中新增)。自PHP 5起本常量返回该类被定义时的名字,大小写敏感。在PHP 4中该值总是小写字母的。__METHOD__:返回当前类的方法名(PHP 5.0.0中新增)。注意与__FUNCTION__的返回有所不同,大小写敏感。__NAMESPACE__:返回当前命名空间名(PHP 5.3.0中新增)。这个常量是在编译时定义的,大小写敏感。魔术方法主要是随着PHP的面向对象特性出现的(也就是在PHP 5之后),主要解决的是PHP在面向对象的思想中所遇到的一些特殊情况,写法方面和魔术变量类似,魔术方法使用两个下划线开头,接下来学习常用的魔术方法。__construct():通用的类构造函数。__destruct():通用的类析构函数。__get(string $name):当试图读取一个并不存在的类属性时被调用。__set(string $name, mixed $value):给未定义的类变量赋值时被调用。__call(string $name, array $arguments):当调用一个不可访问类方法(如未定义或不可见)时,__call() 会被调用。__callStatic(string $name, array $arguments):当调用一个不可访问的静态类方法时,__callStatic()方法会被调用。__toString():当打印一个类对象时被调用,这个方法类似于Java的toString方法。__clone():当类对象被克隆时调用。__sleep():持久化一个类对象时,如果__sleep()方法存在则先被调用,然后才执行序列化操作。这个功能可以用于清理对象,比如你有一些很大的对象,不需要持久化,这个功能就很好用。__wakeup():与__sleep()相反,在反持久化类对象时,如果存在__wakeup()方法,则使用该方法预先准备对象数据。__wakeup()可用在类似于重新建立数据库连接等初始化操作中。__isset():当对未定义的类变量调用isset()或empty()时,__isset()会被调用。__unset():unset一个对象的属性时被调用。如:unset($class->name)。__invoke():当尝试以调用函数的方式调用一个对象时,__invoke()方法会被自动调用。__autoload():区别于以上所有方法,__autoload()并非是一个类方法,而是一个全局方法。在实例化一个对象时,如果对应的类不存在,则该方法被调用,可用于类的自动加载。另外,别忘了所有的魔术方法都需要给予public属性。关于魔术变量和魔术方法的应用如代码清单3-5所示。代码清单 3-5
<?php class ClassA { // 私有变量 private $secret; // 给私有变量赋值 private function setSecret () { $this->secret = "my secrets"; } // 构造函数 public function __construct () { echo "CALL ".__METHOD__."\n"; $this->setSecret(); } // 析构函数 public function __destruct () { echo "CALL ".__METHOD__."\n"; } // 魔术方法 __get public function __get ($name) { echo "CALL __get:".$name."\n"; } // 魔术方法 __set public function __set ($name, $value) { echo "CALL __set:".$name.",".$value."\n"; } // 魔术方法 __call public function __call ($name, $arguments) { echo "CALL __call:".$name.",".print_r($arguments, true)."\n"; } // 魔术方法 __sleep public function __sleep () { echo "CALL ".__METHOD__."\n"; $this->secret = "unknown"; return array("secret"); } // 魔术方法 __wakeup public function __wakeup () { echo "CALL ".__METHOD__."\n"; $this->setSecret(); } } $a = new ClassA(); // 初始化 ClassA $a->attrA = "valueA"; // 赋值不存在的属性 attrA echo $a->attrB; // 获取不存在的属性 attrB $a->hello(1,2,3); // 调用不存在的方法 hello() $b = serialize($a); // 持久化 ClassA var_dump($b); // 打印持久化后的对象 $c = unserialize($b); // 反持久化 ClassA var_dump($c); // 打印反持久化后的对象 ?>以上程序先定义了一个类ClassA,此类定义了一个$secret属性,还定义了我们上面介绍到的一些主要的魔术方法,接下来执行了以下步骤,我们来逐一分析。1)初始化 ClassA:调用构造函数__construct(),并对$secret变量赋值。2)赋值不存在的属性 attrA:调用魔术方法__set()。3)获取不存在的属性 attrB:调用魔术方法__get()。4)调用不存在的方法 hello():调用魔术方法__call()。5)持久化 ClassA:调用魔术方法__sleep(),并隐藏$secret变量的值。6)反持久化 ClassA:调用魔术方法__wakeup(),并恢复$secret变量的值。7)最后回收对象:调用两次析构函数__destruct(),原因是这里产生了两个对象实例,$a和$c。该程序的最终运行结果如图3-4所示,大家可以结合上面的分析来思考。
小贴士:代码清单3-5中的print_r方法的功能主要用于打印PHP数组。
神奇的PHP数组记得多年以前有位做Java开发的同事曾经问过我这样一个问题:“为什么PHP的数组这么好用呢?”当时我觉得不以为然,不过现在回过头来想想,这个问题确实值得好好讨论一下。要解决这个问题,首先我们要理解一点:对于PHP来说,没有集合(Set)、栈(Stack)、列表(List)以及散列(Hash)的概念,所有这些常见的数据结构都在PHP的数组里面实现了。我们先来看一段关于PHP数组操作的代码,如代码清单3-6所示。代码清单 3-6
<?php $arr = array(1,2,3); // 集合用法 echo '$arr[0]:'.$arr[0]."\n"; // 栈用法 array_push($arr, 4); echo '$arr:'.print_r($arr, true); array_pop($arr); echo '$arr:'.print_r($arr, true); // 列表用法 array_push($arr, 4); echo '$arr:'.print_r($arr, true); array_shift($arr); echo '$arr:'.print_r($arr, true); // 散列用法 $arr[3] = 5; echo '$arr[3]:'.$arr[3]."\n"; ?>从代码中的注释可以看出,以上程序分别模拟了集合、栈(后进先出)、列表(先进先出)以及散列数组的用法,大家可以体会一下,该程序的运行结果如图3-5所示。
之前我们讨论的都是最为简单的一维数组,而在实际项目中我们经常使用的是其他组合形式的数组,比如,和数据库表结构所对应的“散列数组列表”的形式,下面我们再来看一个例子,见代码清单3-7。代码清单 3-7
<?php // 散列数组列表 $arr = array( array( "name" => "James", "sex" => "M", "age" => "28" ), array( "name" => "Iris", "sex" => "F", "age" => "27" ) ); ?> <table border=1 cellspacing=1 cellpadding=5> <tr><td>Name</td><td>Sex</td><td>Age</td></tr> <?php foreach ($arr as $row) { ?> <tr><td><?=$row["name"]?></td><td><?=$row["sex"]?></td><td><?=$row["age"]?></td></tr> <?php } ?> </table>在上述例子中,我们演示了一个“散列数组列表”的使用方法,实际上这种数据结构经常出现在我们从数据库中取出数据然后展现到页面表格中去的情况。除此之外,从本例中我们还可以学到如何在PHP中嵌入HTML标签,当然这已经是一种最古老的使用方法了,在实际项目中我们经常使用一些其他的模板引擎来负责展示部分,比如Smarty模板引擎(我们将在3.5节中介绍)。最后我们来看一下本例在浏览器中的运行效果,如图3-6所示。通过以上的介绍和实例学习,相信大家对PHP的数组已经有了一定的了解,这种融合了列表、散列、栈等多种常用数据结构的“超级工具”可以说是PHP的一大发明;当然,PHP数组的用法绝不仅仅只有以上这些,通过日后继续深入学习,我相信你会越来越喜欢这个“编程利器”的。
虽然之前我们已经学习了PHP语言的开发基础,但是应用这些知识来开发项目还远远不够,因为我们在实际的项目中都需要使用“面向对象”的写法来进行编程,如果只懂得基本语法却没有面向对象的编程思想是万万不行的。所以接下来我们将为大家介绍在PHP语言中,我们是如何使用面向对象的编程思路来进行开发的。其实PHP语言的面向对象编程思路和Java非常相似,对象(Object)、类(Class)、抽象类(Abstract Class)、接口(Interface)以及MVC三层封装的用法都大同小异,由于篇幅限制,相同的地方将不再赘述,这里主要给大家介绍一下不同的地方,下面我们将结合面向对象编程思想中的几个重要概念来分析一下。
抽象性要认识面向对象的思想,首先需要理解什么是对象。对象是面向对象编程的基本元素,对象可以是我们需要研究的任何元素,可以是具体的物品,也可以是抽象的事物。比如我们在研发一个后台管理系统时,管理员是一个对象,后台权限也是一个对象。这就是抽象的基本思路,就这点来说,不管我们使用的是什么语言,思考方式应该是一样的。此外,对象在程序代码中是用“类”(Class)来表示的,每个类都需要具备“唯一性”的特征,在大部分语言中都使用“命名空间”来解决这个问题;然而对于PHP来说,我们通常使用一种“约定”或者“规范”来解决这个问题,比如在PHP的一些大型类库(如Zend Framework)中我们通常把类名和目录名对应起来,Zend/Db/Exception.php文件的类名是Zend_Db_Exception,这种方式在项目中还是比较实用的。
继承性继承性是面向对象思想中最基础、最重要的特点之一,也正是因为对象的继承性才延伸出了“抽象”和“封装”等面向对象的设计方法。在PHP语言中我们同样使用私有(private)、保护(protected)和公共(public)关键字来设定类、属性和方法的权限,关于这些基础概念的基本用法大家可以到网上收罗到一大堆,这里不再赘述。下面我们将以一个PHP代码为例给大家讲解一下使用要领,见代码清单3-8。代码清单 3-8
// 基础抽象类 abstract class Base_Customer { // 私有属性 private $_name = ''; // 私有方法 private function _checkName ($name) { return is_string($name) ? $name : false; } // 保护方法 protected function _formatName ($name) { return (string) $name; } // 公用方法 public function setName ($name) { $this->_name = $this->_formatName($name); } // 公用方法 public function getName () { return $this->_checkName($this->_name); } // 抽象方法,子类需要实现 abstract public function setPassword(); // 抽象方法,子类需要实现 abstract public function getPassword(); }上述实例代码中我们定义了一个名为Base_Customer的用户抽象基类,里面有一个名为“$_name”的私有属性,表示用户的名字;为了让外部能够访问这个属性,我们添加了读取名字getName和设置名字setName两个public方法,供外部程序调用。另外,此类还有一个private方法_checkName用于确认用户名字是否有误,一个protected方法_formatName用于保证名字正确性。在代码的最后我们还定义了两个抽象方法,设置密码setPassword和取得密码getPassword两个方法,用于对用户的密码进行操作,这两个方法都需要在子类中实现的。从以上代码中我们可以看到PHP语言里大部分面向对象编程的写法,大家可以好好理解一下。
多态性多态性是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果,这个特性进一步增加了面向对象编程思想的灵活性和重用性。我们知道,重载是实现多态性最常见的方法,比如下面就是使用Java语言来使用重载的实例代码,见代码清单3-9。代码清单 3-9
class Demo{ // 成员变量 int a, b, c; // 构造函数 public Demo() {} // 重载构造函数 public Demo(int a, int b, int c) { this.a = a; this.b = b; this.c = c; } // 成员方法 int sum() { return a + b + c; } // 重载成员方法 int sum(int d) { return a + b + c + d; } }我们看到上面用Java语言实现的Demo类中,构造方法Demo被定义了两次,根据传入参数的不同,方法逻辑也有所不同;另外,类中的成员方法sum也被定义了两次,同样可以根据不同的参数来处理不同的逻辑。但是这是Java的做法,在PHP中我们也可以这样做吗?答案是否定的,因为PHP语言不允许出现相同名称的方法,即使在同一个类中。那么我们应该怎么做呢?看看代码清单3-10中是怎么写的吧。代码清单 3-10
class Demo { // 成员变量 public $a, $b, $c; // 构造方法 public function Demo ($a = 0, $b = 0, $c = 0) { if ($a) $this->a = $a; if ($b) $this->b = $b; if ($c) $this->c = $c; } // 成员方法 public function sum ($d = 0) { if ($d) { return $this->a + $this->b + $this->c + $d; } else { return $this->a + $this->b + $this->c; } } }我们可以看到,以上的PHP代码同样实现了一个Demo类,此类含有和前面Java版的Demo类同样的成员变量,却只有一个构造方法Demo和成员方法sum;但是有趣的是,通过对这两个方法的逻辑分析,我们会发现这里同样根据参数的不同实现了不同的逻辑。这是为什么呢?答案其实很简单,就是因为PHP允许设置参数的默认值。这种PHP特有的功能帮助我们用另外一种方式实现了多态性。以上我们介绍的PHP面向对象编程的基础知识,需要大家好好理解一下,因为在后面我们即将给大家介绍的微博应用实例中,这些用法将会被广泛使用;另外,在本章最后介绍的Hush Framework框架中我们也会接触到这些用法。当然,培养成熟的面向对象的编程思想绝不是一朝一夕的事情,需要大家在学习的时候边实践边思考,最好能阅读一些比较优秀的代码,当然本书后面将要给大家介绍的微博项目实例代码也是个很不错的面向对象编程的代码范本,如果大家能通过本书将其理解透彻,绝对会受益匪浅。
业内常说“不理解会话(Session)的概念就等于不懂得PHP网络编程”。当然,这里讲的是PHP语言用于互联网编程的时候。因为HTTP协议是无状态的,所以每次请求结束后,变量和资源就被回收了,那么我们如何保存一些“持久”的信息呢?比如在用户登录之后,系统需要把用户登录的信息保存下来,在整个应用或者站点里面使用。也许你会想到使用数据库来保存,当然这确实是一种解决方案,但是这些数据大部分属于临时数据,用户退出登录之后就没有用了,如果使用数据库不仅浪费资源,而且还需要定期清理,相当麻烦。为了解决这个问题,PHP专门为我们提供了会话模块,来保存这些临时的用户数据。和大部分的语言环境一样,PHP的Session机制不是非常复杂,客户端只需要保存一个会话ID,即Session ID,每次会话请求都会把这个Session ID传给服务端,并获取服务端接口处理完的数据,整个过程如图3-7所示。
PHP默认的会话存储方式是文件存储,数据会被保存到服务器本地的session.save_path参数设定的目录中(此参数位于php.ini配置文件)。使用的时候,首先需要调用session_start方法开启一个新的Session,然后直接使用PHP预定义变量$_SESSION来进行读取和存储操作,在请求结束时系统会把修改过的会话值保存到存储器中。示例用法如代码清单3-11所示。小贴士:php.ini是PHP的环境配置文件,在Linux系统下一般会被放在/etc/php.ini目录下。该文件几乎包含了PHP运行环境所需的所有配置,也是我们必须学习的内容之一,由于篇幅原因,本书不做详细讲解。具体配置参数可直接参考官方文档,地址如下http://cn.php.net/manual/zh/ini.list.php。代码清单 3-11
<?php session_start(); // 初始化计数器变量 $count $count = isset($_SESSION['count']) ? $_SESSION['count'] : 0; // 计数器依次递增 $_SESSION['count'] = ++$count; // 打印计数器值 echo $count;前面的会话实例实现了一个简单的计数器,逻辑很简单,大家参照着注释就可以很快读懂。这里需要注意的是,Session机制仅适用于有服务器的网络环境中,在以命令行(CLI)脚本运行的情况下是不起作用的。另外,我们可以看到该计数器程序的有效代码只有4行,这也从一个侧面反映了PHP语言的简单高效。当然我们这里讨论的仅仅是比较简单的Session使用场景,对于相对比较大型一点的网络应用来说,Session的使用就不是这么简单了。比如我们要在多台应用服务器之间共享Session,那就不能把Session信息存放在本地了,这时候我们可能需要把Session集中存储在某个公用的中间件里,比如数据库或者缓存服务器等。好在PHP给我们提供了Session回调接口来帮助我们控制Session的存储方式,实例代码请参考代码清单3-12。代码清单 3-12
<?php class SessionHandler { protected $savePath; protected $sessionName; public function __construct() { session_set_save_handler( array($this, "open"), array($this, "close"), array($this, "read"), array($this, "write"), array($this, "destroy"), array($this, "gc") ); } public function open($savePath, $sessionName) { $this->savePath = $savePath; $this->sessionName = $sessionName; return true; } public function close() { // 关闭 Session 逻辑 return true; } public function read($id) { // 读取 Session 逻辑 } public function write($id, $data) { // 存储 Session 逻辑 } public function destroy($id) { // 销毁 Session 逻辑 } public function gc($maxlifetime) { // 回收 Session 逻辑 } } // 使用 Session 类 new SessionHandler();上述实例中,我们使用session_set_save_handler方法重写了PHP的Session机制,通过这种方式我们可以很方便地控制Session的存取逻辑来满足我们的需求;此外,这也是我们优化Session机制时必须使用的知识,这些进阶知识我们会在本书的9.1.2节中给大家做进一步的介绍。
相关资源:新年快乐! python实现绚烂的烟花绽放效果