通过3.1.2节的学习,我们大致了解了PHP的基本语法,本节我们将进一步认识PHP语言。首先,我们来看看PHP语言的几个特色,也就是它和其他语言不大一样的地方。
1. 预定义变量
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所示。
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的过程中一定要特别注意这些数值的使用方法。
3. 魔术变量和魔术方法
最初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数组。
4. 神奇的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数组的用法绝不仅仅只有以上这些,通过日后继续深入学习,我相信你会越来越喜欢这个“编程利器”的。