PHP 5完全攻略
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第2章 PHP 5开发基础

PHP的语法简洁友好,设计开始时借鉴了C、Perl等语言的语法,从PHP 5版本后开始使用类似Java的面向对象语法结构,目前的PHP 5.3版本,已经完全支持面向对象。

我们兴奋地看到,融合吸纳了各个语言优点于一身的PHP,在提供强大功能的同时,仍然秉持着易学易用的特点。在本章中,我们将讨论以下内容:

➢ PHP语句的基本结构

➢ PHP与HTML混合编写

➢ 变量、表达式与基本数据类型

➢ 表单变量以及基本处理

➢ 常量的定义

➢ PHP的分支结构与循环控制结构

➢ PHP自定义函数

➢ PHP时间与日期处理

➢ PHP的网络开发

2.1 PHP初相遇

在第一章中我们已经安装了支持PHP运行的环境,接下来开始准备着手编写第一个PHP脚本。

编写PHP脚本很简单,先要准备好自己喜欢的编辑器,比如前面提到的Eclipse编辑器或Dreamweaver CS,如果你愿意当然也可以是Windows记事本。

现在,我们开始编写第一个PHP脚本程序,在编辑器上创建一个新文件,输入如下代码:

        代码 2-1:start.php – 第一个PHP程序
        <?php
            echo  "我的第一个PHP程序";
        ?>

要记住保存为扩展名为.php的文件,这是非常重要的,用来告诉服务器端的PHP解析器来“解释”文件中的PHP语句并处理。保存好后,还要给它起一个容易记的名字,比如“start.php”。

我们是在自己的本地计算机测试,需要把文件复制到Apache的根目录中(本例为d:\htdocs\www\)。接着请打开火狐或IE浏览器,在地址栏中输入地址:http://localhost/start.php,如果一切没有问题,我们会看到浏览器窗口会显示一段文字:“我的第一个PHP程序”,如图2-1所示。

图2-1

如果您看到的是脚本的源代码,表示PHP没有安装或服务器不识别文件的扩展名,那么需要检查PHP的环境配置,具体请参考第一章的安装配置部分。

刚才我们写的第一个文件是纯粹的PHP脚本。还可以把PHP嵌入到一个HTML文档中,将下面的代码复制到一个新文件,另存为second.php。代码如下:

        代码 2-2:second.php– 嵌入HTML的PHP脚本
      <html>
        <head>
          <title>欢迎您进入php5的世界</title>
        </head>
        <body>
        <?php
          $lang_name = "PHP5";
          echo "欢迎您使用".$lang_name. "<br />";
          echo "<strong>这里使用$lang_name打印文本</strong>";
        ?>
        </body>
      </html>

在浏览器地址栏中输入http://localhost/second.php,在浏览器上会显示如图2-2所示字样。

图2-2 使用PHP输出HTML

我们看到,该页面有一个标题:“欢迎您进入php5的世界”,在窗口中还有粗体字,这是因为文件本身是一个HTML文件,“这里使用PHP5打印文本”是该脚本PHP输出的文字。

可以使用浏览器的“查看源文件功能”来看当前页面的源,会发现都是HTML标签,这是因为PHP已经解析完文件,输出到浏览器的只是纯HTML页面,如图2-3所示。

图2-3 输出的HTML源代码

在这两个PHP脚本中,我们都使用了echo这一语句,用它来输出一段文字或显示变量的内容,接下来还会再用到它,请不要着急,关于这个语句的详细功能我们会逐步深入。

TIPS:什么是脚本(Script)

脚本是某一个程序或脚本引擎执行的命令列表,而不是编译后的计算机处理器指令序列。

脚本用来自动在本地计算机上批量执行命令序列或在网页上执行。比如,VB Script脚本可在Windows机器上运行,AppleScript脚本可以自动执行在Macintosh计算机上的任务,而PHP脚本则通常运行在Web服务器上用以生成动态网页的内容。

2.2 几个技术点

PHP对代码处理非常灵活,为了让代码具有好的可读性,我们刚开始编写程序的时候,要严格遵循语法结构,对每行PHP语句以及程序构造都尽量细致,甚至要多次修正。

2.2.1 PHP语句

一个PHP文件通常包含HTML标记和一些PHP语句段。

一个PHP语句段从“<?php”标签开始到“?>”标签结束,PHP标签用于分隔其他PHP语句段和HTML,PHP语句就写在这两个标签中间,可以写多行PHP语句。

PHP语句通常由一个表达式或一个语句体组成,所有单行语句必须用半角分号(;)作为语句或表达式的结束标志,如果PHP解析器没有找到代码之中的分号,那么它会继续分析该文件,直到找到一个分号为止,并忽略所有空格和空行。如下代码:

      <?php
        echo "我用PHP";
      ?>

当PHP脚本被浏览器访问而执行时,PHP解析器会查看该页面中是否有PHP脚本标签,如果有就按照逻辑顺序解析并执行,完成后通过Web服务器把结果输出到客户端浏览器显示。

2.2.2 空格与空行

在PHP中,回车换行、空格字符及制表符(TAB键)皆被视为空格,当PHP脚本中包含空行或空格字符时,PHP解析器会当这些字符不存在。如下代码:

        代码 2-3:second.php– 嵌入HTML的PHP脚本
      <?php
          echo "PHP ";
          echo "Blank Space";
      ?>

与下面的PHP代码是等价的:

      <?php
          echo "PHP "; echo "Blank Space";
      ?>

我们看到,这里把两行语句写在了一行,中间使用空格分隔。虽然两段脚本的代码和执行结果相同,但很明显第一段代码更易阅读。

2.2.3 注释

代码注释是PHP编程的一部分,目的是方便别人阅读代码。那么,简单的行注释可以写为:

        <?php
          //这是PHP的注释
        ?>

注释的文本从这两个斜线开始,直到回车换行符结束。这一行将被PHP解析器忽略。

此外,行注释也可以写在PHP语句行的后面,如下代码:

        <?php
          echo "欢迎光临";  //输出一段文字
        ?>

除了单行注释,还有多行注释。以/*开始到*/结束,用来描述有关程序或函数的较长信息,一般放于文件顶部,如作者等信息。如下代码:

        代码 2-4:php_comment.php–PHP中使用多行注释
      <?php
        echo "PHP多行注释";
        /*
          多行注释,可以在这里
          写较多的注释信息。
         */
         echo "不会输出到客户端";
      ?>
2.2.4 函数调用

随着编程时间的累积,我们会发现很多工作是重复的工作,比如访客要发邮件给站长、会员注册、会员找回密码等功能,都要对电子邮件地址进行准确性的校验。我们可以在这两个程序都写上相同的代码,但这样实在和复制粘贴没什么两样。PHP提供了这种代码复用的语法结构,它被称为函数。

PHP函数有两种类型。第一类是PHP内置的函数,这些函数已经写好,我们不必自己编写,当要使用这些函数时,我们只需要在脚本中添加一个函数调用代码,它告诉PHP去“呼叫”这个内置函数。

第二种类型是用户自定义函数。这些函数是你自己写的,然后,把你写的函数放在程序中,这样我们在脚本中也同样可以调用我们自己写的函数。

在PHP中调用函数,使用如下格式:

        function_name(参数,参数)

例如,我们调用系统内置的print函数:

        print($message);

其中print是该函数的名称,该函数的功能是打印一串字符串。括号用来给函数传递参数,如果不需要传递任何参数,写一个空的括号即可。

这里$message是我们要传递给print函数的参数。如果函数需要多个参数传递,则在多个参数间使用逗号分隔。

在该例中,参数$message写在函数的括号内,PHP内置的print函数将开始打印$message变量的内容,接着是正常的PHP语句分号,表示函数调用完毕,一行PHP语句结束。

注意:函数通常要返回调用程序需要的数据。例如,print函数会给调用程序返回一个布尔值。

在接下来要讨论的内容里,其中一部分操作就调用了PHP的内部函数,所以在此有必要向各位讲清楚。

2.3 变量与变量名

何谓变量?变量是一个数据存储单元,用来保存数据,由于在程序执行中变量的内容(值)会发生变化,因此被称为变量。

变量名是用来标识变量的,就如同人的姓名,但是在同一个程序中变量名如果相同,最后声明的变量值会把以前的同名变量值覆盖。

在PHP中,变量以美元符号$开头,接着跟着变量名作为标识,最后以分号(;)结束。变量使用等号(=)操作符进行赋值操作。

结合上面一节的脚本例子,请看以下一些变量名的例子:

        $_1name;     //合法,但不推荐,因为命名不规范
        $url;        //合法,推荐使用规范命名
        $user_name;  //合法,推荐使用
        $user&name;  //非法,不允许有&符号
        $163site;    //不合法,变量名不允许数字开头
        $我的变量;   //不合法,PHP5不允许用汉字或多字节字符用作变量名

我们看到,上面有的变量命名理论上是允许的,但在实际开发中却是不规范的,因此要尽量使用标准的英文来命名变量,而不要随意命名(比如有人喜欢用拼音命名),在命名上要说清变量是用来做什么的,必要时可以使用足够长的单词。

下面把PHP变量的命名规则给大家做一个总结:

①PHP变量名之前必须有一个美元符号$。美元符在技术上说并不变量名的一部分,但只有这样,PHP解析器才认为这个字符串是一个变量;

②变量名长度<=255个字符,名称可以由字母、数字0~9和下画线(_)组成;

③变量名不能以数字开头,如$123,这是错误的;

④变量名名称严格区分大小写,如$UserName与$username标识的是两个不同的变量;

⑤为避免命名冲突或混淆,请不要使用与函数相同的名称;

⑥数值型的变量不要求使用单引号(')或双引号引用("),若使用,则被视为字符串类型变量。

另外,我们在编程时,要养成在变量使用时加入注释的良好习惯,这样有利于程序的修改和维护。

在PHP中,变量的命名和赋值是同步进行的。有两种方式为变量赋值,一种是按值传递方式,另一种是引用传递方式。我们接下来就开始了解这些内容。

2.4 变量的赋值

按值传递是将表达式的值分配给变量,这是在任何语言中都会使用的赋值方式。比如:

        $numeric  =  29;
        $string  =  "PHP的世界多精彩";;
        $total = 30*500;
        $age = 29;

每个变量都拥有自己的值,与其他变量相区别,在内存区域中分属独立。比如$numeric变量与$age变量的值虽然相同,但各自属于自己的值,它们相互是独立的。

如果想让这两个变量指向相同的值,我们需要使用引用赋值。

2.5 变量的引用赋值

引用赋值也称关联赋值。“引用”实际就是指两个变量名用了一个相同的内存地址。在PHP中用&符号实现引用。先来看下面的代码:

        代码 2-5:assignment_value.php –使用正常的值传递赋值方式
        <?php
            $a = 5;
            $b = $a;
            echo $b;
        ?>

该程序首先创建一个变量$a,它的值是5,然后再将它的值赋值给变量$b,此时$a的值与$b的值相同,如果随后改变$a的值,$b的值不会随着改变,代码如下:

        代码 2-6:assignment_value2.php –使用正常的值传递重新赋值
      <?php
          $a = 5;
          $b = $a;
          $a = 7;   //为$a变量重新赋值为7
          echo $b;
      ?>

此时,$b仍然是5。如果使用&引用符号,就可以使它们同步。代码如下:

        代码 2-7:use_reference.php –使用引用赋值方式
      <?php
          $a = 5;
          $b = &$a;    //实现引用赋值
          $a = 7;
          echo $b;
      ?>

这里我们使用&符号来引用$a变量的地址,当$a变量的值被修改为7时,$b变量的值也会被同步更新为7。

2.6 PHP的数据类型

PHP最初源于Perl语言(http://www.perl.org),与Perl类似,PHP对数据类型也采用较为宽松的处理。

PHP规定,变量数据类型会根据程序中的逻辑自动设置。这意味着,在PHP脚本运行时,用户有时候甚至没有机会决定一个变量的内容是一个数值还是字符串,亦或是其他数据类型。

虽然这一点很灵活,但有时也会出现一些小问题:如果程序员不将某个变量指定为所需要的数据类型,那么在PHP脚本运行时,它可能会导致你得到的数据类型并不是你想要的数据类型,从而导致计算错误的发生甚至出现比较难以恢复的错误。

尽管如此,你也大可不必担心。我们已经知道PHP的这种内部自动设置数据类型的机制,在开发过程中,对于变量、函数的返回值以及不能明确执行的数据类型,我们可以将其强制声明为自己需要的数据类型。

下面,我们开始了解PHP的数据类型。PHP的数据类型共分为三类:标量变量类型、复合数据类型和特殊数据。

2.7 标量数据类型

标量变量指的是只保存单一的数据信息,标量变量的数据类型主要有4 种,分别为:整型,浮点型、字符串型和布尔型。

2.7.1 整型

在PHP中,整型变量称为integer或int类型,用来保存数字,可以保存整型或浮点型以及八进制、十六进制数据。如下表达式:

      $numeric  = 32;

我们使用等号给变量赋值,在这里,变量$numeric的值被分配为32。整型数据包括正整数和负整数,不包括小数点部分,所以这个变量是一个整型变量。

整型数据还可以表示为八进制和十六进制的数据。

其中,八进制数基数为8,由3位数字组成,由前缀0以及0~7组成来表示一个数,如下表达式:

      $numeric = 040;

十六进制数的基数为16,其16位字符由数字0到9和字母A到F组成,前缀由“0x”组成,“0x”不区分大小写,如下表达式:

      $numeric = 0xff;
2.7.2 浮点型

浮点型变量在PHP中被称为float类型,也称为实数,分为单精度浮点型数据与双精度浮点型数据。如下表达式:

      $numeric = 5.2;

这里$numeric变量的值是5.2,保存的是一个浮点型的数据,有1位小数位。也可以像这样:

      $numeric = 3.1415;

浮点型数据还可以表示指数的形式,比如:

        $numeric = 3E4;

这表示$numeric变量的值为3*104。

2.7.3 字符串型

当变量的内容是使用双引号(")和单引号(')来作为定界符时,都属于字符串类型变量,简称为字符串,在PHP中表示为string类型。

下面的字符串使用的是双引号作为定界符:

        $char  = "B座 2003";

我们再使用单引号作为字符串的定界符,如下:

        $char = '2012';

可以看到$char的内容虽然是数字,但使用了单引号来作为定界符,因此也是字符串类型变量。

2.7.4 布尔型

布尔型变量只有两种值:True或False,意为逻辑真和逻辑假。如:

        $OK = TRUE;

这里变量$OK就是布尔型变量。布尔型变量通常用于条件表达式以及语句块的判断处理,如if分支语句,while循环语句等。

下面的布尔型变量写法都是正确的,不区分大小写,如:

        $OK = tRuE;    //语法无误,但不规范
        $OK = true;
        $OK = True;

2.8 复合数据类型

复合数据类型允许将多个类型相同的数据聚合在一起,表示为一个实体项。复合数据类型包括数组(Array)和对象(Object)。

2.8.1 数组

数组在PHP中的英文表示为Array。数组是一系列相关的数据以某种特定的方式进行排列而组成的集合,组成这个集合的各个数据可以是基本数据类型,也可以是复合数据类型;可以是相同的数据类型,也可以是不同的数据类型。

每个数组索引成员(也称为键-Key)引用相应的值,可以是一个作为编号使用的数字序列,也可能是一些直接以单词命名的值。

例如,我们创建一个名为state的数组,用来保存国家名称,我们创建一个索引数组,代码如下:

        $state[0] = "中国";
        $state[1] = "美国";
        $state[2] = "英国";
        ...
        $state[260] = "墨西哥";

如果想让数组与项目有关联,而不是原来的数字与值的简单关系,比如中国省份与省会的关系,可以这样来写:

        $province['AnHui'] = "安徽";
        $province['SiChuan'] = "四川";
        $province['LiaoNing'] = "沈阳";
        ...
        $province['HongKong'] = "香港";

可以看到这种数组的索引(键)是由英文描述的,并且与保存的值有一定关联,因此这种数组称为关联数组。

声明数组后,数组中的元素个数可以自由更改,只要给数组赋值,数组会自动增加长度。值得一提的是,PHP也支持数组的多层结构,即可以保存更多数据的多维数组。

如果你不完全理解这些内容,也请不要担心,这些概念会在本书第5章中作详细介绍,只要记住PHP支持数组类型即可。

2.8.2 对象

复合数据类型的另一个类型是对象,对象在PHP中的英文表示为Object。对象在面向对象编程中是一个核心概念。

在面向对象语言中,人们把各个具体事物的共同特征和行为抽象成一个实体,称之为一个“类”,与PHP中其他数据类型不同的是,一个对象必须实例化声明后才能使用。

下面是一个类的声明和随后的对象实例化的一般例子:

在PHP中,类的定义格式如下:

        class phpClass {              //定义类的名称为phpClass
          public $var1;               //成员变量
          function getArg ($arg1) {   //定义类的成员
              $this->var1 = $arg1;
              //etc
          }
          //etc
        }

创建好类后,我们使用“new”关键字来实例化一个类并得到该类的一个对象。脚本如下:

        代码 2-8:object.php– 创建类和实例化调用
        <?php
          /* 创建一个类,名为Books */
          class  Books{
              /* 成员变量 */
              public $price;
              public $title;
              /* 成员方法 */
              function setPrice($par){
              $this->price = $var;
              }
              function getPrice(){
              echo $this->price ."<br/>";
              }
              function setTitle($par){
              $this->title = $par;
              }
              function getTitle(){
              echo $this->title ." <br/>";
              }
            }
            $phpbook = new Books;   //新建实例
          $phpbook->setTitle( "PHP5与MySQL5 Web开发技术详解" );
          $phpbook->setPrice( 89 );
          $phpbook->getPrice();
      ?>

在一个类中,需要定义几个属性和相关的方法(函数)。在本例中类的名字为Books,它有两个属性$title和$price,用于保存书名和单价。

在该类中有两个用于修改属性的方法,分别是setTitle和setPrice方法,还有一个读取类中属性的方法getPrice。

要记住,类定义的是一个模板,本身不能被操作。只有通过new关键字创建基于此模板的实例对象后才能操作。

该脚本执行结果如下:

        PHP5与MySQL5 Web开发技术详解
        80

如果您不了解面向对象编程,也不用担心,我们会在第9章面向对象开发中做全面的讨论。

2.9 特殊数据类型

在PHP中,有用来专门提供服务或数据的数据类型,它不属于上述标准数据类型中的任一类,因此也被称为特殊数据类型,主要包括NULL和资源数据类型。

2.9.1 NULL

NULL在PHP中是一种特殊的数据类型,它只有一个值,即NULL,表示空值,注意它与空格的意义不同。

当下列条件满足时,变量的值为NULL:

1.变量被指定为NULL值;

2.变量在没有被赋值前,默认值为NULL;

3.使用unset($var)函数删除一个变量后,这个变量值也为NULL。

空类型的数据只承认一个值——NULL,类似于下面的变量定义:

        $var = NULL;
2.9.2 资源

资源(Resource),在PHP中同样是一种特殊的数据类型。它主要描述一个PHP的扩展资源,例如,一个数据库查询(Query)、一个打开的文件句柄(fopen)或一个数据库连接(Database Connection)以及字符流(stream)等扩展类型。

但是程序员并不能直接操作这个变量类型,只可以查看内容或在函数返回时使用。

如果上面所说的某一种情况出现时,例如MySQL数据库的连接,我们可以使用var_dump()函数打印连接时的变量内容,就会显示一个类似Resource#2的内容,这便是资源。请看如下代码:

        代码 2-9:resource.php– 显示资源的内容
      <?php
        $db = mysql_connect('localhost','root','root'); //$db变量就是一个资源类型
        var_dump($db);  //打印$db内容
      ?>

如果连接成功,该脚本执行后会显示为:

        resource(2) of type (mysql link)

这个ID号为2,表示MySQL已经连接成功。如果我们的MySQL账号不对,除了提示连接失败外,该资源类型的内容会返回一个布尔值False。

资源是PHP提供的较强特性之一,它可以在PHP脚本中做自定义的扩展,类似于C语言结构中的引用,它的所有属性都是私有的,您暂可以理解为面向对象中的一个对象实例化。

有关资源类型数据,我们在后续字符流一章的内容中会再次讨论。

2.10 变量打印与输出

前面的一些代码示例中我们使用了echo输出文字或变量内容,与echo功能类似的还有print、printf、sprintf这四个函数,这几个函数的共有功能就是在浏览器或终端上显示或输出内容。

你也许会问,PHP有这么多输出语句,哪个好,有哪些区别?下面我们一起讨论。

2.10.1 使用echo函数

格式:void echo (string arg1 [, ...string argN])

使用echo语句可以显示变量和字符串,代码示例如下:

        代码 2-10:echo.php– 使用echo语句输出文字和变量内容
        <?php
          $a = 12345;  //给$a变量赋值,内容是整型值
          $b = "this is string <br />";  //给$b变量赋值,内容是字符串
          //下面分别显示两个变量内容
          echo $a;
          echo $b;
          echo $a,"和",$b,"<br />";
          echo "<strong>PHP变量</strong>";  //HTML文本
          echo
        ?>
2.10.2 使用print函数

格式:int print(string$arg)

print函数的功能与echo语句的功能类似,都是输出变量或字符串的内容。请看如下代码示例:

        代码 2-11:print.php– 使用print函数输出文字
        <?php
          $version = 5.2;
          print "当前PHP版本 $version";
        ?>
2.10.3 echo和print的区别

echo函数单纯显示文本信息或变量值,没有返回值;

print(包括printf、sprintf)函数除了显示之外,还会返回一个布尔值(True或False);

echo函数没有返回值,但是可以使用逗号(,)和小圆点(.)来显示字符串,而print函数则只能用小圆点来连接显示,不能使用逗号来接收多个参数。如:

        代码 2-12:echo_print_diff.php – echo与print函数的区别
        <?php
          echo '甲骨文', '宣布74亿美元收购Sun公司.';
          echo '甲骨文'. '宣布74亿美元收购Sun公司.';
          print '甲骨文'. '宣布74亿美元收购Sun公司.'; //如果用逗号,print函数会出现语法错误
        ?>

如果我们单纯显示文字,使用echo函数的效率要比print函数高。

2.10.4 使用printf函数

格式:boolean printf(string format[,mixed args])

printf函数用于格式化输出字符串,主要用于字符串中以%开头的格式字符串替换。

其中,“%d”表示按整数输出。

printf("%d只基金节后派发", "41");

这段代码将输出:41只基金节后派发。

“%ld”表示按十进制长整型输出,“%s”表示按字符串输出,“%f”表示按浮点数输出。

        代码 2-13:printf.php – 使用printf输出格式化字符
      <?php
        printf("%s(Avatar)由詹姆斯·卡梅隆执导,二十世纪福克斯出品,全球票房超过%d亿美元" , "阿凡达" , 10);
        //运行结果:阿凡达(Avatar)由詹姆斯·卡梅隆执导,二十世纪福克斯出品,全球票房超过10亿美元
        printf("歌曲%s", "You Are Not Alone"); //运行结果:You Are Not Alone
        printf("%01.2f", 43.2);             // 运行结果:43.20
      ?>

如果是%1或%2这种类型的格式字符串,则按顺序进行参数替换显示,替换后的数据类型显示使用$表示,如%s换为$s。如下:

        代码 2-14:printf2.php – 使用顺序替换的printf函数
        <?php
            printf("2010年1月,%2\$s准备收购 %1\$s"," Yelp "," Yahoo! ");
            //运行结果: 2010年1月, Yahoo! 准备收购Yelp
            printf("The %1\$s says: %2\$s,%2\$s.","dog","bark");
          //运行结果:The dog says: bark,bar.
        ?>
2.10.5 使用sprintf函数

格式:string sprintf(string format,mixed [args]...);

sprintf函数也用做字符串格式化。该函数与printf函数基本相同,但它可以将转换后的结果保存到一个字符串变量中,而不是直接输出。

其中参数format是转换的格式,以百分比符号%开始到转换字符为止。请见下面的脚本例子:

        代码 2-15:sprintf.php – 使用sprintf函数保存格式化后的变量
        <?php
            $var1 = 68.75;
            $var2 = 54.35;
            $var3 = $var1 + $var2;
            // 变量 $var3 值为 "123.1";
            $formatted = sprintf ("%01.2f",$var3);
            // 变量$var3 值为 "123.10"
        ?>

其中:%01.2f的%符号是指定格式开始,也就是从“起始字符”开始,直到出现“转换字符”,格式化字符的工作正式结束。

在%符号后面的0表示“填空字符”,如果位置为空就用0来填充。在0后面的1规定小数点前面的数字占位要有1位以上,把1换成2,若$var3的值为1.23,则$formatted的值将为01.23。由于在小数点前面的数字只占了1位,按照上面所规定的格式,小数点前数字应该占2位,现在只有1位,所以用0来填满。在%01后面的.2的意思是规定小数点后的数字,必须占2位。如果$money的值为1.234,则$formatted的值将为1.23。为什么4不见了呢?因为在小数点后面按照上面的规定,必须且仅能占2位。可是$var3的值中小数点占了3位,所以4被去掉了,只剩下23。

最后,以f“转换字符”结尾,其他转换字符请参考表2-1的字符转换列表。

表2-1 Printf和sprintf所支持的格式转换字符表

如果在%起始符号后面加上-(负号)则会把数字按左对齐的方式进行处理,如下例所示:

        代码 2-16:sprintf2.php – 使用sprintf函数左对齐数字
        <?php
            $money = 1.4;
            $formatted = sprintf ("%-02.2f",$money);
            echo $formatted;
        ?>

这时候,$formatted将不会再是01.40而是1.40。

转换的格式依次包括如下:

①填空字符。0的话表示空格填0;空格是默认值。

②对齐方式。默认值为向右对齐,负号表示向左对齐。

③字段宽度。为最小宽度。

④精确度。指在小数点后的浮点位数。

2.10.6 显示数组与对象

数组和对象是PHP中的复合数据类型,在PHP开发中会经常使用,下面了解一下如何使用函数来打印数组的所有内容。

1.使用print_r

格式:print_r($array/$var)

print是打印的意思,而r则取自Array的单词,那么该函数的功能就是打印数组内容,它既可以打印数组内容,也可以打印普通的变量。如下所示:

        print_r($_REQUEST);
        print_r($_GET);        /* 打印使用GET方法传递的表单内容 */
        print_r($_POST);       /* 打印使用表单POST方法传递过的数组内容 */

2.使用var_dump

格式:var_dump($object/$array/$var)

var代表变量(Variable),变量包括对象、数组以及标量变量,dump的英文有倒出之意,加在一块,就是将变量或对象的内容全部输出来的意思,如下例:

        var_dump($DB);         /* 打印$DB数据库连接对象的内容*/
        var_dump($fileHandle); /* 打印文件句柄对象的内容 */
        var_dump($Smarty);     /* 打印Smarty模板对象 */

3.使用var_export

格式:var_export($object/$array/$var)

输出或返回一个变量的字符串表示。此函数返回关于传递给该函数的变量的结构信息,它和print_r()类似,不同的是其返回的表示是合法的PHP源代码。可以通过将函数的第二个参数设置为TRUE,从而返回变量的表示。例如:

        代码 2-17:var_export.php – 使用var_export返回PHP数组源格式
        <?php
          $a = array (1, 2, array ("a", "b", "c"));
          var_export ($a);
          echo "<hr>";
          $v = var_export($a, TRUE);
          echo $v;
        ?>

上面脚本中,$v = var_export($a, TRUE)表示返回的将是PHP的源代码,可以直接用在PHP脚本的数组定义中。

相关说明:

以上三个函数都可以打印对象的值、系统函数值以及数组的内容,但也有一些区别,如下:

➢ echo、print、printf可以打印变量内容,但不能显示数组及系统超级变量数组。

➢ print_r和var_dump不仅可以打印数组、标量变量,还可以打印对象的内容。

➢ var_dump语句不仅能打印变量、数组内容,还可以显示布尔变量和资源(Resource)的内容。

➢ var_export函数返回关于传递给该函数的变量的结构信息,与var_dump()函数类似,不同的是其返回的内容是合法的PHP代码。

2.11 变量操作符

和其他编程语言一样,在PHP中提供变量之间的加、减、乘、除以及字符连接等操作符,有符号形式(如+和-)和符号的组合(如++和+=),下面我们按照类型来依次讨论。

2.11.1 算术运算符

PHP的运算符有常用的算术运算符,与平常使用的数学公式类似,我们来举个例子,下面是个简单的表达式:

        1 + 3;

在这个表达式中,我们使用了一个操作符(即'+')和两个操作数(数字1和3)。在'+'操作符两边增加了两个操作数的值(结果值为4)。

我们来看表2-2对算术运算符的完整描述。

表2-2 PHP算术运算符说明

其中:除法(“/”)操作总是返回浮点数,即使两个运算数是整数(或由字符串转换成的整数)。

2.11.2 赋值操作符

在脚本中,我们可以写一个1+3的算术表达式,或者写成$a+$b,这样写在PHP中也不会出错,但只计算不赋值可能没有什么意义,还需要把计算后的结果赋值给变量。PHP使用一个“=”号用作对变量赋值操作,我们来看如下代码:

        $numeric = 0;                 //赋值为0
        $string = "test string";      //赋值为一个字符串

还可以直接进行运算后赋值,我们需要有一个变量,然后用等号这个赋值操作符连接,形成一个表达式,如下:

        $myVar= 1 + 3;

这段语句会把计算后的值4,分配给变量$myVar。

此外,PHP中的运算级别与C语言优先级相同:即优先计算括号中的表达式,然后再按常规的算术计算规则进行运算,如:

        $a = 10;
        $b = 8;
        $a = 6 * ($a +$b);

这段语句将先计算括号中表达式的值再和6相乘,然后再把结果赋值给变量$a。

值得一提的是,在PHP中也可以使用复合赋值操作符,来看下面的表达式:

        $a += 8;

它等价于下列赋值表达式:

        $a = $a + 8;

同理,我们也可以使用*=,/=,%=操作符进行乘法、除法、取余的复合运算。

2.11.3 字符串连接

在PHP编程中,有时会将几个字符串连接成一个字符串,字符串的连接使用小圆点(.)操作符,然后再进行变量赋值或显示。我们来看下面的代码:

        代码 2-18:stringpoint.php –使用小圆点连接字符串
        <?php
            $a = '你好 ';
            $b = 'PHP5';
            echo $a.$b;  //字符串连接并赋值给$c
        ?>

输出的结果是“你好PHP5”,小圆点将$a和$b两个变量连接起来,这样可以避免编写多个echo语句,提高开发效率。

当连接的两个变量都是数字型,且使用小圆点连接时,PHP会将其视为字符串型变量,来看如下代码:

        代码 2-19:stringpoint_num.php –使用小圆点连接数值类型变量
        <?php
          $num1 = 22;
          $num2 = 9;
          echo $num1 . $num2;
        ?>

连接后的结果是“229”。

2.11.4 字符串换行连接

换行连接也是个复合操作,与标准连接很相似,只不过,如果一个字符串很长,或在处理循环时不同字符串的连接,需要做换行连接。

我们还是使用小圆点(.)与赋值操作符(“=”,等号)来连接两个相同名称的变量,使之成为一个字符串,如下代码:

        代码 2-20:stringpoint_wrap.php –使用小圆点连接字符串
        <?php
            $str  = '迎接上海';
            $str .= '2010\’世博会';  //把$str连接在一起
            echo $str;
        ?>

这段代码的输出方式和上面的一致。当遇到一个过长的代码影响可读性,或者动态连接几个变量或表达式时,会使用换行连接操作。

2.11.5 花括号的作用

另外PHP还有一个常用的花括号“{}”分隔符,结合双引号也可以实现小圆点相同的效果,我们来改写2-20的代码。

        代码 2-21:curlybrackets_slash.php –使用花括号连接与区分变量
        <?php
            $str = "迎接上海";
            $str1 = '2010\'世博会';
            echo "{$str}{$str1}";
        ?>

该脚本与上面例子执行结果相同。当两个变量紧挨着或变量混在普通文本中时,PHP会将变量和普通文本混为变量,使用花括号可以区分变量与普通文本的功能。如下代码:

        代码 2-22:not_use_curlybrackets.php –在字符串中直接显示变量
      <?php
        $a = "Simple";
        $b = "Application";
        $string = "Here is an $a $bs";
        echo $string;
      ?>

脚本执行后结果如下:

        Here is an Simple

该脚本的第3行语句,是连接$a和$b字符串变量,而之后$b与s字符相邻,PHP理解为输出$bs变量的内容,因此只输出了$a的值,而$bs变量的值为空,所以出现此结果。

我们可以在$b变量后加一个空格或使用小圆点来连接,但空格会影响输出的效果,小圆点可以解决该问题,但要多输入一些分隔符,代码如下:

        $string = "Here is an ".$a. " $b"."s";

而使用花括号就可以让这种任务轻松完成。如下代码:

        代码 2-23:use_curlybrackets.php –使用花括号连接和区分变量
      <?php
        $a = "Simple PHP";
        $b = "Application";
        $string = "Here is an {$a}{$b}s";
        echo $string;
      ?>

脚本执行后结果如下:

        Here is an Simple PHP Applications

花括号除了有和小圆点一样的优点外,再有就是可以显示数组的索引值,如果PHP设置语法严格,这个分界符将非常有用,在后面的脚本中会多次用到。

2.11.6 单引号与双引号

前面我们讨论过,字符串变量可以使用双引号(")声明,也可以使用单引号(')声明。

注意,这两个引号的声明有一些不同,其主要区别是:如果字符串中包含一个变量,使用双引号时,PHP会显示字符串中这个变量的内容,而使用单引号时,PHP会将整个字符串包括这个变量,都视为纯文本。

以下面的PHP脚本代码来说明:

        代码 2-24:quote.php – 单引号与双引号的异同
        <?php
          $user = "VIP";
          $str = "你好$user";         //双引号字符串中包含变量名称$user
          $str1 = "你好$user_name";  //双引号字符串中包含变量名称$user_name
          $str2 = '你好$user';       //单引号字符串中包含变量名称$user
          echo $str;                    //$str输出变量内容
          echo '<br />';  /             //输出一个HTML换行符
          echo $str1;
          echo '<br />';
          echo $str2;
        ?>

我们使用浏览器访问该脚本,会输出如下内容:

      你好VIP
      你好
      你好$user

我们会发现$str1变量只输出了“你好”两个字,这是因为$user_name这个变量在前面没被赋过值,虽然这里使用了双引号,PHP要显示这个变量的内容,但是这个变量没有内容,是个NULL空值。

2.11.7 HereDoc

HereDoc允许在PHP脚本中嵌入一些大的文本内容,比如一些电子邮件模板、一些HTML或文本内容,因此该语句在这些方面非常有用,请见下面的代码例子:

        代码 2-25:heredoc.php – 使用HereDoc
        <?php
            echo <<<THIS_HEREDOC
            PHP stands for "PHP: Hypertext Preprocessor".
            The acronym "PHP" is therefore,usually referred to as a recursive acronym because the
            long form contains the acronym itself.
        THIS_HEREDOC;
        ?>

以<<<THIS_HEREDOC开头,以前面的关键字THIS_HEREDOC做对应,表示引用结束。

在<<<后面的名字可以是任何你喜欢的名称,比如可以用“HELLO”定义HereDoc开始,然后末尾就以“HELLO”表示HereDoc语句的结束。

在HereDoc中可以直接引用PHP变量(前提是该变量已经定义),HereDoc会解释该变量,直接显示该变量的值,为避免与其他文字混淆,可以用花括号将该变量括起来,代码如下:

        代码 2-26:heredoc_brackets.php – 使用HereDoc与花括号
        <?php
            $output = "PHP5的世界";
            $content = <<<THIS_HEREDOC
              欢迎使用{$output}
        THIS_HEREDOC;
            echo $content;
        ?>

请注意,在使用HereDoc时,结束符的前面不要用空格或TAB字符进行缩进操作,在后面也不要有任何空格字符,否则PHP会提示解析错误,这在前面几个例子中已经有所体现。

如果在Heredoc内容中要显示$开头的字符,PHP会认为是一个从$开始到文本末尾的变量名,由于不是合法的变量标识,它会提示错误,因此需要进行字符转义操作。

2.11.8 转义操作符

何谓转义?转义就是在一个字符串内容中,当其中的字符与该字符串的分隔符相同或存在类似变量的$字符时,需要对它们加以区分,告诉PHP这个字符是一个普通字符。

转义操作符很简单,用“\”来表示转义,注意是左斜线。我们来看如下脚本:

        代码 2-27:stringpoint_slash.php –对类变量字符使用转义操作符
      <?php
        $str = "迎接上海2010年世博会";
          echo "$str变量的内容是$str";
      ?>

执行该段脚本后会显示“迎接上海2010年世博会变量的内容是迎接上海2010年世博会”,这是因为双引号解释$str的内容。我们是想显示“$str变量的内容是迎接上海2010年世博会”的字符串,可以在第一个$str变量前面加入转义操作符:

        echo "\$str变量的内容是$str";

有时候字符串中会出现与定界符相同的情况,如果出现这种情况也需要进行转义操作,请看下面代码:

        代码 2-28:stringpoint_slash2.php –对引号使用转义操作符
        <?php
            $str = "迎接上海";
            $str1 = '2010\'世博会';
            echo $str.$str1;
        ?>

该脚本的输出结果是:

      迎接上海2010’世博会

可以看到字符串中2010 后加入了“\”转义操作符,这里字符串与变量定界符相同,都是单引号,使用了转义操作符来告诉PHP解释器,这是一个普通字符。

再看一个PHP中输出一个Javascript时的转义用法。

        代码 2-29:stringpoint_slash3.php –对引号使用转义操作符
        <?php
            echo("<script language=\"JavaScript\">\n");
            echo("document.write(\"使用php输出的Javascript.\\n\")");
            echo("</script>\n");
        ?>

可以看到除了对与定界符相同的符号——双引号进行转义外,对Javascript的换行也做了转义。

关于转义,还有addslashes()和addcslashes()两个函数,这两个函数允许我们手工指定字符串中某个字符加入转义符。

2.11.9 格式控制符

有些特殊的控制符号也可做一些简易的格式控制,如换行,输出一个TAB操作符等。关于PHP的特殊控制符号与说明详见表2-3。

表2-3

换行控制符对于浏览器的显示几乎没有什么影响,只会在文本文件输出时起作用,如对HTML文件中每行标签的换行等。

我们在12章中还会使用这些格式控制符。

2.11.10 递增与递减操作符

下面是比较有趣的递增和递减操作。

我们使用++和--两个操作符实现对一个变量值的递增(加1)和递减(减1),这个操作符在计算及for循环语句中会时常用到。请看下面代码:

        代码 2-30:operator_incdec.php –前置增减与后置增减运算
        <?php
          $a = 12;
          $b = 8;
          echo "总和: ", $a + $b++;
          echo "<br />";
          echo "b变量的值: ". $b;
          echo "<br>a变量的值: ". --$a;
        ?>

该脚本打印出来的结果为:

        总和: 20
        b变量的值: 9
        a变量的值: 11

首先计算“$a+$b++”表达式的值,++位于变量之后表示先计算后递增,因此总和是20,这时$b的值已经递增了1,再打印$b变量的值就是9;接着开始显示--$a的值,--操作符位于变量之前表示先递减再计算,所以12递减1之后的值为11。

关于递增与递减运算符规则如表2-4所示。

表2-4

2.11.11 三元操作符

三元操作符的格式:

格式1 :<布尔表达式>?<值1>:<值2>

格式2 :<布尔表达式>?<表达式1>:<表达式2>;

如果布尔表达式的值为真(True),此表达式的值为表达式1 或值1 的值,如果布尔表达式的值为假(False),则此表达式值为表达式2或值2的值。来看下面的脚本例子:

        代码 2-31:three_operand_operator_value.php –使用三元操作符
        <?php
          $grade = 80;
          $result = ($grade>=60? "及格":"不及格");
          printf ("成绩: %d <br />", $grade);
          printf ("结论: %s <br />", $result);
        ?>

根据三元操作符的运算逻辑,$grade>=60表达式为真,因此这个脚本将输出如图2-4所示结果。

图2-4

下面我们在三元操作符中使用表达式,脚本代码如下:

        代码 2-32:three_operand_operator_exp.php –使用三元操作符
       <?php
          $x = 15;
          $y = 7;
          $z = ($x > $y)?($x / $y):($y * $x);
          printf ("x = %d <br>", $x);
          printf ("y = %d <br>", $y);
          printf ("z = %d <br>", $z);
       ?>

该脚本的执行结果如图2-5所示。

图2-5

使用三元操作符的好处是编写起来简洁,但没有使用if分支语句可读性好,有关if语句我们稍后讨论。

2.11.12 比较运算符

比较运算符是用于处理两个操作数的关系运算。

当操作数是两个字符串时,相比较的关系是按字典字母顺序处理;当操作数是数字时,按数字大小比较,比较后返回一个布尔值真(True)或假(False)。PHP算术运算符与说明如表2-5所示。

表2-5

我们来看一个使用比较运算符的脚本示例,代码如下。

        代码 2-33:comparison_operators.php –对引号使用转义操作符
        <?php
          $s = 100;
          $a = 50;
          $y = "5000 PHP软件工程师";
          $i = "我";
          $o = "你";
          printf ("$s == $a :%d <br>", $s==$a);
          printf ("$s != $a :%d <br>", $s!=$a);
          printf ("$s <> $a :%d <br>", $s<>$a);
          printf ("$s === $y :%d <br>", $s===$y);
          printf ("$s !== $y :%d <br>", $s!==$y);
          printf ("$s > $a :%d <br>", $s > $a);
          printf ("$i > $o :%d <br>", $i > $o);
          printf ("$s < $a :%d <br>", $s < $a);
          printf ("$i < $o :%d <br>", $i < $o);
          printf ("$s >= $a :%d <br>", $s >= $a);
          printf ("$i >= $o :%d <br>", $i >= $o);
          printf ("$s <= $a :%d <br>", $s <= $a);
          printf ("$i <= $o :%d <br>", $i <= $o);
        ?>

脚本执行结果如图2-6所示。

图2-6

2.11.13 布尔运算符

布尔运算符也被称为逻辑运算符,运行时将两个变量或表达式比较后的结果转换为布尔值。表2-6 为布尔运算符对操作数的处理规则和用法。

表2-6

其中“与”和“或”操作有两种不同形式运算符的原因是操作的优先级不同。请看下面的脚本例子:

      代码 2-34:boolean_operators.php使用布尔运算符
      <?php
        $q = 16;
        $r = 7;
        $s = 17;
        $t = 17;
        $u = ($q > $r) AND ($s > $t);
        $v = ($q > $r) && ($s > $t);
        $w = ($q > $r) OR ($s > $t);
        $x = ($q > $r) || ($s > $t);
        $y = ($q > $r) XOR ($s > $t);
        $z = ($q < $r) XOR ($s > $t);
        $a = ($q > $r);
        $b = !$a;
        printf ("(q > r) AND (s > t) = %d <br />",$u);
        printf ("(q > r) && (s > t) = %d <br />",$v);
        printf ("(q > r) OR (s > t) = %d <br />",$w);
        printf ("(q > r) || (s > t) = %d <br />",$x);
        printf ("(q > r) XOR(s > t) = %d <br />",$y);
        printf ("(q < r) XOR (s > t) = %d <br />",$z);
        printf ("(q > r) AND (s > t) = %d <br />",$u);
        printf ("(q > r) = %d <br />",$a);
        printf ("!a = %d <br />",$b);
       ?>

该脚本执行结果如图2-7所示。

图2-7 使用布尔运算符

2.11.14 位操作符

位操作符是将一个整型变量当作一系列的二进制bit(位)来处理,常在加密处理与用户权限处理时使用,PHP位操作符与说明如表2-7所示。

表2-7

使用位运算符的示例代码如下:

      代码 2-35:bit_operators.php使用布尔运算符
      <?php
          printf("7 & 15 = %d <br>", 7 & 15);
          printf("7 | 15 = %d <br>", 7 | 15);
          printf("7 ^ 15 = %d <br>", 7 ^ 15);
          printf("~7= %d <br>", ~7);
          printf("7 << 1 = %d <br>", 7 << 1);
          printf("7 >> 1 = %d <br>", 7 >> 1);
      ?>

以上脚本执行后的结果如图2-28所示。

图2-8 位运算符的应用

2.12 数据类型自动转换

前面我们提到过PHP在变量定义方面比较自由,脚本中使用变量前可以不用声明,也不需要声明它的数据类型,因此PHP被称为弱类型的语言,也称动态语言。

在脚本运行过程中,PHP会根据程序运算逻辑自动为变量转换数据类型。这和C、Java等这些强类型语言有很大不同,比如在Java中,则必须改变其类型,否则会出现值丢失或类型不匹配等问题,而在PHP中做这样的操作,则不会有此问题,比如:

        echo 2 +'10个马铃薯';

这个表达式开始执行后,PHP解析器会认识到,它是一个数字相加的操作,因此两个操作数都应该是数字,其中一个已经是数字,不需要转换,而另一个操作数却不是数字,需要转换。

关于PHP对非数字的转换,有一些规则:比如表达式中“10个马铃薯”这个字符串,PHP解释器会从左至右扫描它,如果最左侧里面有相邻的数字字符,将被认为是个数字,否则会视为0,因为最右侧是10,因会被转换为10。

因此,以上表达式执行后的计算结果是12。再看下面的表达式:

        echo 1 + '没有1个马铃薯';

根据规则,这个表达式最后结果是1,后面的字符串被转换成数字0,因为最左侧没有数字。

值得一提的是,布尔值、NULL和Resource(资源)在被显示或计算时,也会被PHP转换为可显示的整型值或字符串。我们来看下面的脚本例子:

        代码 2-36:auto_boolean.php – 自动转换布尔类型
        <?php
            $num_1 = 1;
            $num_2 = '0'; //变量为字符串类型
            //PHP会把$num_2自动转换成数值类型,并做判断
            $result = $num_2 == 0? "参数不能为0":"参数正确";
            echo $result;
        ?>

这个表达式的结果是个布尔值,在这段代码中,如果$num_2==0,表达式的计算结果是TRUE,则if结构块里的代码开始被执行。该脚本的输出结果如下:

        参数不能为零

一个标量变量也可以作为布尔表达式,请看下面的脚本例子:

        代码 2-37:inttoboolean.php – 标量变量转换为布尔值
        <?php
            $num_1 = 1;
            $num_2 = '5';
            if ($num_2) {
            echo "参数正确";
            } else {
            echo "参数不能为零";
            }
        ?>

执行的结果如下:

        参数正确

在这个脚本中,PHP解释器自动转换了$num_2变量的类型,以尽可能地计算精确,变量$num_2的值5被视作布尔值True,因此执行了if语句块中的语句,输出上面的结果。

我们把数据类型自动转换规则总结如下:

➢ 布尔值是False时显示为0,布尔值是True时将显示为1;

➢ NULL空值显示为0;

➢ Resource显示为字符串:The resource’s # (id)。

不要因为上面的自动转换类型误解PHP的运算精度,在计算精度方面,我们还可以把变量强制指定为某种数据类型,也就是数据类型的强制转换。

2.13 数据类型强制转换

PHP强制类型转换可以通过两种方式进行,一种是在变量前面加上一个小括号,并把想要强制转换的数据类型写在括号中,另一种方式是用settype函数来实现。

关于使用括号加类型的强类型转换,可由下面的代码说明:

      代码 2-38:casting_operators.php- 使用括号进行强类型转换
      <?php
        $var = 2.25;
        $var = (int)$var;  //把$var变量类型强制转换为整型(int)后重新赋值给$var
        echo $var;
      ?>

这里的(int)$var表达式就是把$str变量强制转换为整型(int)数据,该脚本的执行结果为2。

下面我们使用settype函数进行相同功能的转换。脚本如下:

      代码 2-39:casting_settype_operators.php- 使用settype运算符
      <?php
        $var = 2.25;
        settype($var, 'int');
        echo $var;
     ?>

该脚本执行的结果与上面的例子相同,都是2。

请各位留意,虽然两个脚本执行的结果相同,但是两者转换的机制存在一些区别:

settype()函数是把原来的变量基础转换类型,转换后直接修改了变量值;而强制类型转换将生成一个变量副本,而原来的变量的类型不变。在第一个脚本中,我们把强制转换后的值又重新赋值给了$var变量,这相当于覆盖原有数据,有时候我们是直接显示或使用另一个变量来保存转换后的值。

PHP中强制类型转换格式与结果如表2-8所示。

表2-8

上表为强制转换数据类型的格式,可以根据需要,使用括号+数据类型形式或settype函数进行强制转换。

2.13.1 转换为整型数据

当需要将其他数据类型转换为整型数据时,其规则如表2-9所示:

表2-9

当将双精度数值转换为整型数时,会作取整操作,而且不会四舍五入,如:

        $var = 6.9;
        $var2 = 8;
        echo (int)$var+$var1;

结果为14,后面的0.9将被舍去。如果需要高精度的运算结果,就不能采取这种强制类型转换的方式。

2.13.2 用intval()函数取整

与强制类型转换为整型方式相类似,intval()函数也是舍掉小数点以后的数值取得整数数值。注意,intval()是直接取整,不会进行四舍五入操作。

如果变量开始就含有数值的字符串,也可以从字符串的开始处提取到整型数值,如果不是数字开头,将取值为0。例如:

        intval(22.56);          //结果是22
        intval("30 May,2006");  //结果是30
        intval("May 30,2006");  //结果是0

当一些变量的小数点后面的数字对我们没意义时,可以使用intval()函数来做取整处理。

2.13.3 转换为浮点型数据

当其他数据类型强制为浮点型时,PHP会在小数位以及数值方面做一些补充和转换,其规则如表2-10所示。

表2-10

2.13.4 转换为布尔型数据

当其他数据类型数据强制转换为布尔型时,所有数据类型会转换为两种值,一种是布尔值True,一种是布尔值False,如表2-11所示。

表2-11

2.13.5 转换为字符串型数据

当其他数据类型数据强制转换为字符型时,数值型数据将被视为普通字符串,其他类型的数据会做相应的转换规则,如表2-12所示。

表2-12

2.13.6 转换为数组

当和数组操作,如合并、比较时,也会把标量变量转换为数组,PHP会尽量把标量变量进行无损转换,对于如对象之类的复合数据,会做一些相应的规则转换,如表2-13所示。

表2-13

2.13.7 转换为对象

在使用面向对象编程时,有时候需要把其他数据类型转换成对象,规则如表2-14所示。

表2-14

数据类型强制转换一般在标量变量类型中使用,为了读者方便,我们将转换规则汇总成表格,您不必全部记住,当需要了解转换后的数据精度或规则之时,再对比以上表格即可。

2.14 超级全局数组

超级全局数组(Super Global Array)是由PHP内置的,不需要开发者重新定义。

在PHP脚本执行时,PHP会自动将当前脚本需要收集的数据放在超级全局数组中,如表单提交的值、用户会话、系统环境等内容。

在PHP脚本、函数及类中都可以直接使用这些超级全局数组。PHP的超级全局变量数组列表如表2-15所示。

表2-15

表格中的$_GET、$_POST、$_REQUEST与网页中的表单有关,如表单中使用POST方法提交数据后,那么该数据会保存到$_POST超级全局数组中。

$GLOBALS数组的内容包括PHP中所有的超级全局数组。我们使用var_dump函数来打印当前环境中$GLOBALS超级全局数组的内容:

        代码 2-40:var_dump.php- 使用var_dump打印超级全局数据内容
        <?php
          var_dump($GLOBALS);
        ?>
        显示结果如下:
        array(5) {
          ["GLOBALS"]=>
          array(5) {
            ["GLOBALS"]=>
            *RECURSION*
            ["_POST"]=>
            array(0) {
            }
            ["_GET"]=>
            array(0) {
            }
            ["_COOKIE"]=>
            array(0) {
            }
            ["_FILES"]=>
            array(0) {
            }
          }
          ["_POST"]=>
          array(0) {
          }
          ["_GET"]=>
          array(0) {
          }
          ["_COOKIE"]=>
          array(0) {
          }
          ["_FILES"]=>
          array(0) {
          }
        }

从显示结果中可以看出刚刚提到的几个类型的超级全局数组都已经被打印出来了,除了$_FILES,其他5个数组还同时包含在$GLOBALS数组的二维列中。

因为当前运行的脚本环境中没有相应的表单值传递,所以有的数组值是空,一旦有内容传递,比如用户提交了表单,就可以使用下面的代码,显示超级全局变量数组以及传入的内容:

      代码 2-41:list_superglobal_array.php- 显示超级全局数组
      <html>
        <head>
          <title>显示系统超级变量数组内容</title>
        </head>
        <body>
          <h2>$_GET - GET请求变量</h2>
          <pre><?php print_r($_GET); ?></pre>
          <h2>$_POST - POST请求变量</h2>
          <pre><?php print_r($_POST); ?></pre>
          <h2>$_COOKIE - Cookie变量</h2>
          <pre><?php print_r($_COOKIE); ?></pre>
          <h2>$_FILES – 文件上传变量</h2>
          <pre><?php print_r($_FILES); ?></pre>
          <h2>$_SERVER – 服务器环境变量</h2>
          <pre><?php print_r($_SERVER); ?></pre>
          <h2>$_ENV – 系统环境变量</h2>
          <pre><?php print_r($_ENV); ?></pre>
          <h2>$_REQUEST – 所有的请求变量</h2>
          <pre><?php print_r($_REQUEST); ?></pre>
          <h2>$GLOBAL – 所有的公共变量</h2>
          <pre><?php print_r($GLOBAL); ?></pre>
        </body>
      </html>
2.14.1 $_SERVER超级全局变量数组

$_SERVER超级全局变量数组也被称为服务器环境变量,用来保存Web服务器设置、客户端请求信息或者当前脚本的执行环境信息等。

$_SERVER是一个关联数组,常用的键名与功能说明如表2-16所示。

表2-16

我们可以使用以下代码,打印当前系统下$_SERVER超级全局数组的所有内容。

      代码 2-42:list_server_superglobal.php- 打印$_SERVER超级全局数组的内容
      <?php
          while (list($var,$value) = each ($_SERVER)) {
          echo "$var => $value <br />";
          }
      ?>

以下是打印的结果(笔者的操作系统是Windows Vista,可能与您安装的环境稍有不同),其中里面的大写字符,均是$_SERVER超级变量数组中的内容(请参见脚本中的注释),代码如下。

      HTTP_ACCEPT => */*
      HTTP_ACCEPT_LANGUAGE => zh-cn //接受的语言
      HTTP_UA_CPU => x86
      HTTP_ACCEPT_ENCODING => gzip,deflate //可以接受的压缩解码方式
      //当前浏览器请求时发送的USER_AGENT
      HTTP_USER_AGENT => Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR
      2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; InfoPath.2; Maxthon 2.0)
      HTTP_HOST => localhost  //当前请求的主机名
      HTTP_CONNECTION => Keep-Alive
      PATH => C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files\Common
      Files\Lenovo;C:\Program Files\Lenovo\Client Security Solution;C:\Program
      Files\MySQL\MySQL Server 5.2\bin
      SystemRoot => C:\WINDOWS
      COMSPEC => C:\WINDOWS\system32\cmd.exe
      PATHEXT => .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
      WINDIR => C:\WINDOWS
      SERVER_SIGNATURE =>
      SERVER_SOFTWARE => Apache/2.2.14 (Win32) PHP/5.2.6 //Web服务器以及PHP版本
      SERVER_NAME => localhost //服务器主机名
      SERVER_ADDR => 127.0.0.1 //服务器绑定的IP地址
      SERVER_PORT => 80 //服务器响应的端口号
      REMOTE_ADDR => 127.0.0.1 //客户端访问的IP地址
      DOCUMENT_ROOT => F:/www  //Web服务器的根目录绝对路径
      SERVER_ADMIN => info@21cto.com
      SCRIPT_FILENAME => F:/www/test.php //当前文件运行的绝对路径
      REMOTE_PORT => 1076 //客户端访问时使用的端口号
      GATEWAY_INTERFACE => CGI/1.1 //CGI版本
      SERVER_PROTOCOL => HTTP/1.1  //访问的HTTP服务协议
      REQUEST_METHOD => GET //通过何种方式请求,本例为GET,通过地址栏传递参数
      QUERY_STRING =>   //通过GET方式传递的URL字符串
      REQUEST_URI => /test.php //当前地址栏的URI地址
      SCRIPT_NAME => /test.php //当前脚本名称
      PHP_SELF => /test.php  //指向脚本的文件名称
      REQUEST_TIME => 1155828897

如果是用Apache的模块方式运行PHP,还可以用函数getallheaders()直接打印客户端HTTP请求头中的信息。代码如下:

      代码 2-43:getallheader.php- 打印HTTP头信息
      <?php
          $headers = getallheaders();
          foreach ($headers as $name => $content) {
          echo "headers[$name] = $content<br>\n";
          }
      ?>

和getallheaders相关的函数还有:apache_lookup_uri()、apache_response_headers()和fsockopen(),我们在后续章节会作相关介绍。

2.14.2 修改全局数组的值

根据需要,还可以动态修改超级全局变量数组里的内容,下面的例子是修改环境变量$_ENV['COMPUT ERNAME']中的值。

      代码 2-44:modify_env.php- 修改系统环境变量的值
      <?php
          echo $_ENV['COMPUTERNAME'] . "<br />";     //显示当前$_ENV环境变量中的计算机名称.
          $_ENV['COMPUTERNAME'] = "php5.2 production server"; //重新设置计算机名称
          echo $_ENV['COMPUTERNAME'] . "<br />";
      ?>

结果如下:

        ThinkPad_x60
        php5.2 production server

2.15 变量与脚本处理

2.15.1 使用isset()函数检测变量定义

格式:isset($var)

该函数用于检查$var变量或对象中的属性是否定义,它返回一个布尔值。我们可以使用isset()函数检查数组中的某一元素是否有值或定义。类似于如下形式:

        if (isset($_POST["user_name"])) {
            //...
        }
        也可以使用isset()函数检测一个对象的属性是否定义。
        if (isset($obj->property)){
            //...
        }

在上面两个例子里,如果变量或属性没有定义,isset()函数将返回布尔值False,反之则返回布尔值True。

isset()函数还可以对多个变量是否定义进行检查:

格式:isset($var1,$var2,$var3,...);

这时如果括号中以逗号分隔的变量都已定义过,将返回布尔值True,如果其中有一个变量未定义,就会返回布尔值False。

这个函数经常用于处理网页上的表单,用来检查表单是否有提交,当客户端有提交动作时,判断HTML表单上按钮上的某个变量是否创建,就可以知道用户是否提交了表单中的某个文本框或按钮。

2.15.2 使用unset()函数删除变量

格式:unset($var)

unset()函数的功能是取消某个变量的定义,即删除变量,并释放其所占用的内存空间。代码如下:

      代码 2-45:unset.php- 使用unset函数
      <?php
        $var_name = "PHP5 变量";
        unset($var_name);
        $result = isset($var_name)? "变量未删除":"变量已删除";
        echo $result;
      ?>

这个脚本的输出结果是“变量已删除”。

2.15.3 使用empty()函数检测变量值是否为空

格式:empty($var)

表示如果$var的变量值为空,empty()函数则返回True。

注意,当变量的值为NULL(无值)时,函数仍将变量表示为空,这会使程序运行出现问题。如果是在不确认的情况下,可以使用($var!=‘‘)这种表达式来检查变量的内容是否为空更为稳妥。

empty()函数常用于判断表单上某一文本域是否填写,相关内容我们会在第5章中详细讲解。

2.15.4 其他检测类函数族

PHP中有一系列用于测试变量性质相关的函数族,这些函数可用于确定一个变量的类型。

这些函数包括is_integer(),is_numeric(),is_string(),is_array(),is_bool(),is_float(),is_object()以及is_null()和is_resource(),这些函数都遵循相同的命名规则,用于测试变量是属于某种数据类型。

在前面,我们已经了解了PHP的几种数据类型和函数名称匹配,我们不难理解这些函数对应测试的数据类型。比如,is_array()用于判断变量的类型是否为数组,is_integer()用来判断变量类型是否为整型,而is_numeric是用来判断变量类型是否为数字,依此类推。

另外还有一个检测类函数is_scalar(),它用于检测变量是否是标量变量,如果变量是数组或对象,它将返回布尔值False。请见下面的例子:

      代码 2-46:check_var.php- 使用is_*检测函数族
      <?php
          $var = 98;
          echo "变量 \$var是否为数组: ".is_array($var)."<br />";
          echo "变量 \$var是否为整数: ".is_integer($var)."<br />";
          echo "变量 \$var是否为数字: ".is_numeric($var)."<br />";
          echo "变量 \$var是否为标量变量: ".is_scalar($var)."<br />";
      ?>

结果如下:

      变量 $item是否为数组:
      变量 $item是否为整数: 1
      变量 $item是否为数字: 1
      变量 $item是否为标量变量: 1

这些函数的共同特点就是根据测试的类型,都返回布尔值True或False。我们在脚本中使用的echo显示布尔变量的值,PHP根据上下文自动转换为数字后进行显示。

2.16 常量

所谓常量,即在PHP脚本被定义后,它的值不能被更改。

通常将常量作为一个应用程序的配置信息或保存为不变化的值,如标志位,参数配置信息等。

2.16.1 定义常量

在PHP中,定义常量使用define()函数来实现。

格式:define("CONSTANT_NAME",常量值)

其中:

➢ "CONSTANT_NAME" ——表示常量名称,一般是一个大写字符串;

➢ 常量的值可以定义为字符串或数值;

➢ 常量的值可以定义为布尔值(True/False),若定义为布尔值,默认值为True;

➢ 所有常量的作用范围都是全局作用域。

常量与变量的重要区别在于:定义或引用常量时,常量没有$美元符号,另外,常量只可以保存整型、浮点型、字符串型或布尔值。

下面我们使用define()函数来定义一系列常量,脚本如下:

      代码 2-47:define_constant.php- 使用define函数定义常量
      <?php
          define("APP_URL" , "www.21cto.com");
          define("APP_PATH" , "/usr/local/data/www.21cto.com/");
          define("DB_USER" , "root");
          define("DB_PASS" , "o$uO012");
          define("STATUS" , true);
      ?>

这些常量类似于定义一个网站的参数配置,再看下面引用常量的例子:

      代码 2-48:use_constant.php- 引用常量的值
      <?php
          define("PI",3.141592);
          echo "PI的值为: ".PI.".<br />";
          $pi2 = 2 * PI;
          echo "PI的平方值为:".$pi2."<br /> ";
      ?>

引用常量只需使用它的名称即可,这个例子中使用PI作为计算的常数。

由于常量在PHP中具有全局作用域,包括在函数和类中。我们可以使用它做一个标识位(flag),用做布尔值校验,脚本例子如下:

      代码 2-49:check_constant.php– 布尔表达式中引用常量的值
      <?php
          define("MY_ERROR" ,1);
          if ($error_code == MY_ERROR) {
          echo "遇到错误";
          }
      ?>

另外,PHP还有一些预定义的魔术常量,下面我们讨论这些魔术常量的功能和使用方法。

2.16.2 PHP魔术常量

所谓魔术常量,就是这个常量保存着PHP脚本运行时的状态,如当前脚本名称、运行的行号等内容,我们可以根据需要引用这些常量值。表2-17为PHP的“魔术常量”列表。

表2-17

我们来看如下脚本:

      代码 2-50:magic_constant.php- 使用魔术常量
      <?php
        class magic_contstant {
        function showMagicConstant() {
          echo "__LINE__ = " . __LINE__ . "<br>"; //当前行数
          echo "__FILE__ = " . __FILE__ . "<br>"; //当前文件所在路径
          echo "__FUNCTION__ = " . __FUNCTION__ . "<br>"; //当前函数名称
          echo "__CLASS__ = " . __CLASS__ . "<br>"; //当前类名
          echo "__METHOD__ = " . __METHOD__ . "<br>"; //当前当前的方法名称
          //echo "__DIR__ = " . __DIR__ . "<br>"; //当前文件夹路径,PHP 5.3支持
          //echo "__NAMESPACE__ = " . __NAMESPACE__ . "<br>"; //当前名称空间,PHP 5.3支持
        }
       }
        magic_contstant::showMagicConstant();
      ?>

脚本执行结果为:

        __LINE__ = 4
        __FILE__ = E:\book\chapter-2\magic_constant.php
        __FUNCTION__ = showMagicConstant
        __CLASS__ = magic_contstant
        __METHOD__ = magic_contstant::showMagicConstant

该脚本定义了一个类,名为magic_constant,用showMagicConstant方法打印了这几个魔术常量的内容。如果你对面向对象有疑惑,这不要紧,这个例子仅仅是为了让你了解这些魔术常量的功能。

2.17 PHP的控制结构

PHP的控制结构在语法上和C语言最接近,类似结构的语言还有Java、C++、C#以及JavaScrpt。

控制结构用于控制与处理代码的流程,根据不同的条件判断,然后去执行或处理不同的业务逻辑。

2.17.1 分支结构语句

分支语句又称为条件判断语句,即根据布尔表达式,执行相应的语句段,完成不同的逻辑操作。

1.if语句

我们在本章的变量操作符中讨论过布尔表达式,if语句就是根据布尔表达式的真值,来判断是否满足条件来执行某项操作。

if语句的结构如下:

        if(<布尔表达式>){
            //语句体
        }

当布尔表达式为TRUE时,就去执行花括号中的语句体。if语句执行的流程如图2-9所示。

图2-9 if语句执行的流程

请看下面使用if语句的脚本代码:

      代码 2-51:if.php– 布尔表达式的值与if分支语句
      <?php
        $score = 90;
        if ($score > 80){
          echo "你的成绩很好哦";
        }
      ?>

在上面脚本中,我们使用比较运算符来判断$score变量的值是否大于80,如果条件为True,if语句将打印“你的成绩很好”。

另外当if、elseif或else的执行语句只有一行时,可以省略花括号“{}”,如:

      if ($score > 80)
        echo "你的成绩很好哦";

为了提高代码的可读性,建议加上花括号。

2.if…else语句

使用if处理,条件为True时执行,但当如果条件不满足(False)时,想执行另一个分支,即二选一的时候,那就要使用if…else语句。if…else语句结构如下:

      if(<布尔表达式>){
            //语句体1
      }else{
            //语句体2
      }

if…else语句执行流程如图2-10所示。

图2-10

当布尔表达式的值为真时执行语句体1中的代码,如果为假则执行语句体2中的代码。示例代码如下:

      代码 2-52:else.php- 使用if…else语句
      <?php
        $hour = 9;
        if ($hour < 10){
            echo "早上好";
        }else{
            echo "上午好";
        }
      ?>

if…else语句适合于2选1的条件,它只会产生两种结果。如果有多种可能的条件,就需要使用if…elseif…else来考虑每个可能的输出。if…elseif…else语句结构如下:

      if(<布尔表达式>){
        //语句体1
      }else{
        //语句体2
      }elseif(<布尔表达式>){
        //语句体3
      }

其中,elseif语句可根据条件灵活调整顺序。if…elseif…else执行的流程如图2-11所示。

图2-11 if…elseif…else语句执行流程

下面是一段猜数字的程序,使用了if…else…if语句。

      代码 2-53:if_else_if.php- 使用if…else…if语句
      <?php
          $secretNumber = 453;
          $guess = 442;
          if ($guess == $secretNumber) {
            echo "<p>恭喜您,猜对了!</p>";
          } elseif ($guess - $secretNumber) < 10) {
            echo "<p>您猜的数字已经很接近了!</p>";
          } else {
            echo "<p>对不起,您的答案与正确答案有段距离哦。</p>";
          }
      ?>

另外值得一提的是,“elseif”的另一种写法为“else if”,二者的作用是等价的。

2.switch语句

当大于3个条件的时候,用if语句就显得比较臃肿,这时就需要使用switch语句了,switch语句用于多个分支的转移执行。其格式如下:

      switch(<布尔表达式>){
          case条件1:
            执行过程
            break;
          case条件2:
            执行过程
            break;
              .....
          default:
            执行过程
            break;
      }

switch语句用于多条件时的分支处理,比if…elseif分支语句的效率要高。通常,break语句放在case语句后。

虽然不是强制地加入break,但如果没有case表达式后的执行语句,它会继续执行,一直找到default语句,如果没有default语句,将一直执行到case最后。

switch语句的执行流程如图2-12所示。

图2-12

请看如下脚本例子:

      代码 2-54:switch.php- 使用switch语句处理多路条件
      <?php
          $answer = 'Y';
          switch ($answer) {
          case 'Y':
          case 'y':
            echo "您的答案是yes\n";
            break;
          case 'N':
          case 'n':
            echo "您的答案是no\n";
            break;
          default:
            echo "错误: $answer不是一个有效的答案\n";
            break;
        }
      ?>

来看一个实际应用的例子,根据用户的浏览器语言自动转向雅虎相应语言的网站,如果对方使用的是繁体中文系统,则转到以BIG5或相应的定制页面,如果对方使用的是韩文浏览器,则转到韩文版网页,实现自动切换语言的作用。

可以在IE浏览器中作如下设置,单击“工具”→“Internet选项”→单击“语言”命令,出现如图2-13所示界面。

图2-13

单击“添加”按钮,可以选择朝鲜语、日语、中文(台湾)等选项。可以将某个语言首选项设置为最高优先级。这样浏览器就设置了多语言选项,根据优先级的顺序,则有可能是“zh-cn,en;q=0.8,ko;q=0.5,zh-tw;q=0.3”。

服务器端的PHP会把客户端浏览器的语言保存在超级全局数组$_SERVER['HTTP_ACCEPT_LANGUAGE']列中,它的值类似于“zh-cn”、“zh-tw”。

根据这一特性,就可以根据用户浏览器所使用的语言版本,自动转到相应语言的网站内容,请见下面的脚本:

      代码 2-55:check_browse_language.php- 使用switch语句判断浏览器的语言
      <?php
        $lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
        $lang = str_replace(strstr($lang, ','), '', $lang);
        //根据来源语言来分别处理
          switch ($lang) {
          case 'zh-cn' :  //如果是简体中文
              header('Location: http://cn.yahoo.com/');
              break;
          case 'zh-tw' :   //如果是繁体中文
              header('Location: http://tw.yahoo.com/');
              break;
          case 'ko' : //如果是韩语
              header('Location: http://kr.yahoo.com/');
              break;
          case 'jp' : //如果是日语
              header('Location: http://www.yahoo.co.jp');
              break;
          default:
              header('Location: http://www.yahoo.com/');
              break;
          }
      ?>

header()函数用以发送一个HTTP头信息,告诉浏览器做什么动作,本例中为重定向到新的URL地址。在之后的内容中,我们还会介绍该函数,使用其他不同的格式和参数,来完成更多的应用。

2.17.2 循环结构语句

循环控制结构用于在程序中需要反复执行的任务。如从数据库中取得记录集放于一个数组,然后循环该数组,并以表格或其他格式化的形式显示出来。

另外,循环语句可以实现嵌套,用于处理较为复杂的任务。

1.while循环

while循环语句的结构格式为:

        while(布尔表达式){
          //语句体
        }

while语句根据条件表达式的值来决定是否开始循环,若表达式的值为True,则循环体内的语句段开始执行。

接着再次循环,如果表达式为False,则该循环将立即结束,语句终止执行,并跳转到循环语句后面的语句执行。

while语句的执行流程如图2-14所示。

图2-14 while语句的执行流程

举例说明,我们要实现阶乘的计算,使用while循环语句实现(将$n变量包含一个数字,如果满足条将开始计算),请看下面的脚本代码:

      代码 2-56:while_factorial.php- 使用while实现阶乘
      <?php
          $result = 1;
          while ($n > 0){
          $result *= $n--;
          }
          echo "结果为 $result";
      ?>
      输出如下内容:
      结果为 1

再看下例,我们要实现对一个文本文件进行读取,而且只取该文件的前5行,使用while循环来实现。

      代码 2-57:while_openfile.php- 使用循环读取文本文件
      <?php
          $linecount = 1;
          $fh = fopen("./contents.txt","r");     //打开文本文件
          while (!feof($fh) && $linecount<=5) {  //如果当前文件没有到末尾,且行数小于等于5 的条件满足
              $line = fgets($fh, 4096);           //读取内容
              echo $line. "<br />";               //显示
              $linecount++;                       //读取下一行
          }
      ?>

2.直到型循环:do…while

do while被称为直到型循环,使用的格式为:

        do{
         //语句体
        }while(条件表达式);

它和while循环语句相似,也是根据条件进行循环的处理。稍有不同的是,它会直接先进入do后的语句段执行,执行完毕后再根据语句段后的布尔表达式的值进行处理。

如果条件为真(True),则继续返回do开始处继续执行do{…}中间部分的语句段,如果值为假(False),则立即结束循环,执行下面的语句。

也就是说,不管条件表达式值如何,do…while都会执行一次语句块中的内容。do…while语句的执行流程如图2-15所示。

图2-15

下面是使用do…while循环的脚本代码。

      代码 2-58:do_while_factorial.php- 使用do…while实现阶乘
      <?php
        $i = 0;
        do {
          echo $i;
        } while ($i > 0);
      ?>

该脚本会先执行do花括号里的语句,然后判断条件是否为真,如果为真则续执行do里面的语句,否则退出。这个脚本会在打印出一个值“0”后,退出循环体。

3.for循环

for语句根据条件表达式的值来处理,如果它为True,则根据起始表达式的值开始循环,增量开始增加值,如果布尔表达式仍为True,则继续循环下去,直到布尔表达式为False时退出循环。

for循环语句的语法为:

      for(初值表达式;条件表达式;计数器/表达式){
          //语句体
      }

如果条件表达式返回True值,则for循环从初值开始,执行循环中的语句体,循环结束后,修改计数器的值,如果条件表达式仍为True,则继续循环,否则退出。

其中:

➢ 初值表达式通常是一个计数器初值,即循环开始的值;

➢ 条件表达式在每次循环时都会被测试,如果条件为False,则立即退出循环体;

➢ 表达式2用于循环执行完毕后,马上修改计数器的值。

for语句执行的流程如图2-16所示。

图2-16

我们来看下面的脚本例子:

      代码 2-59:for_loop_square.php- 使用for计算平方值
      <?php
          for ($i = 0; $i < 10; $i++) {
          print " $i的平方值是: " . $i*$i . "\n";
          }
      ?>

运行结果为:

      0 的平方值是: 0
      1 的平方值是: 1
      2 的平方值是: 4
      3 的平方值是: 9
      4 的平方值是: 16
      5 的平方值是: 25
      6 的平方值是: 36
      7 的平方值是: 49
      8 的平方值是: 64
      9 的平方值是: 81

使用下面的循环也是允许的,但这个例子比较特殊,它相当于一个必须要执行的循环,如果语句中没有break中断语句,很可能会变成死循环,应该避免。请见如下代码:

      代码 2-60:for_infinite_loop.php- 使用for实现死循环
      <?php
          for (;;) {
          echo "这是一个无限循环,将一直运行下去\n";
        }
    ?>

下面讲述for语句的嵌套循环,就是for循环中又包含了另一个for循环,因此被称为嵌套循环。

假设想写个程序,该程序唯一能用的命令是echo,以及for循环,要打印出十排星号(*),每排二十个星号的图案。

使用for的循环嵌套就可以实现该功能,代码如下:

      代码 2-61:for_print_star.php- 使用for循环打印二十排星号
      <?php
          //打印10行
          for ($i=1; $i<=10; $i++){
          for ($j=1; $j<=20; $j++){
            //每行20个星号,开始打印
            echo "*";
          }
          //每行以换行符结束
          echo "<br />";
          }
      ?>

里层的for循环用于打印出每排二十个*号,打印后换行,外层的for循环用于前面的动作连续做十次,就可以打印出十行。

同理,下面的脚本是使用for语句打印一个12行*12列的HTML表格。

      代码 2-62:for_print_table.php- 使用for显示一个HTML表格
      <table border=2>
      <?php
          for($i=1;$i<=12;$i++){
            echo("<tr>");
            for($j=1;$j<=12;$j++){
                printf("<td align=right>%d",$i*$j);
            }
            echo("\n"); //用于HTML代码的换行
          }
      ?>
      </table>

在PHP中输出表格是比较常见的,读者请结合HTML与本例,试编写输出不同样式的表格。

4.foreach循环

使用foreach循环语句的格式有两种。第一种格式如下:

      foreach (数组as $value) {
        //语句体
      }

前面介绍过的for语句对数组遍历的例子,都可以换成foreach语句来完成,请看下面的例子:

      代码 2-63:array_foreach.php- 使用foreach遍历数组
      <?php
          $links = array("www.mysql.com","www.php.net","www.apache.org");
          echo "<b>PHP在线资源</b>:<br />";
          foreach($links as $link) {
          echo "<a href=\"http://$link\">$link</a><br />";
          }
      ?>

该脚本将把数组中的内容依次取出并显示,结果如下:

      PHP在线资源:
      www.mysql.com
      www.php.net
      www.apache.org

使用foreach语句对数组遍历以及循环,有时要明显快于使用for的语句,这是由于PHP在内部为foreach做了非常多的优化工作,因此我们在开发中尽量使用foreach。

值得一提的是,foreach只能对数组和对象遍历,不能根据条件表达式来处理循环,这就是while/for循环语句存在的原因。foreach语句还有第二种格式:

      foreach(关联数组as $key => $value) {
        //语句体
      }

这种格式是foreach循环专门针对关联数组(Associative Array)处理用的,来看下面的脚本例子:

      代码 2-64:associative_array_foreach.php- 使用foreach遍历关联数组
      <?php
          //保存一个学生成绩的关联数组
          $students = array("chinese" => 80,
                            "english" => 73,
                            "math" => 45);
          foreach ($students as $subject => $score){
            echo "各科成绩: $subject = $score<br />";
          }
      ?>

可以看出,foreach在一部分循环处理时是相当方便的,但是它不像for/while语句那样,可以使用条件表达式控制循环的次数,因此在实际开发中,可以根据程序逻辑灵活选择合适的循环结构来完成任务。

5.循环控制:break与continue

有时,需要在循环语句执行中途终止或继续操作,包括for、while以及foreach循环,都可以使用continue与break语句。

continue语句的功能是在跳过continue下面的所有语句重新开始循环。

break语句的功能是强行终止所在语句体的循环操作。

比如从1000中随机生成一个数字,然后判断它是否是素数,如果不是则输出,请见下面的代码:

      代码 2-65:break.php- 使用break语句终止循环
      <?php
          //素数数组列举
          $primes = array(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47);
          for($count = 1; $count++; $count < 1000) {
          //随机生成1~50之间的数字
          $randomNumber = rand(1,50);
          if (in_array($randomNumber,$primes)) {
            echo $randomNumber;
            //中断循环
            break;
          } else {
            echo "<p>在 $randomNumber数字中没有找到素数</p>";
          }
          }
      ?>

执行结果这里从略,因为随着随机数产生的不同,结果也会不同。

而continue语句的功能是跳过continue下面的所有语句,继续进行下一轮的循环操作。请看下面使用continue语句的脚本例子:

      代码 2-66:continue.php- 使用continue提前进入下一轮循环
      <?php
          $usernames = array("孙大东","赵二刚","roger","Ericli","佚名","raymond");
          for ($x=0; $x < count($usernames); $x++) {
          if ($usernames[$x] == "佚名"){
              //如果是"佚名"则跳出本次循环
              continue;
          }
          echo "公司成员: $usernames[$x] <br />";
        }
      ?>

执行结果如下,会发现“佚名”已经从$usernames数组中被排除掉了。

      公司成员: 孙大东
      公司成员: 赵二刚
      公司成员: roger
      公司成员: Ericli
      公司成员: Raymond

break与continue语句可以同时用在循环语句中,如下面代码:

      代码 2-67:break_continue.php- 同时使用break和contine语句
      <?php
        $a = 0;
        do
        {
          if( $a == 5 ) continue;
          if( $a == 8 ) break;
          echo $a . "<br />";
        } while( $a++ < 10 );
      ?>

该脚本执行过程是从0开始加1,一直累加到7,在do循环体中有一个if判断语句,如果循环语句执行到$a变量等于5时,不用显示,直接开始新的循环,如果$a变量等于8时,则使用break语句终止循环操作。

2.18 包含控制语句

代码包含控制结构是如何组织程序的源代码很重要,不仅仅是它们允许组织程序块,而且可以利用它们查到程序一些冗余的地方。

2.18.1 include与require

在实际项目开发中,常常需要根据业务逻辑写一些类或函数保存到独立的文件中,然后再根据需要使用包含语句将相关类文件或函数文件引入。

比如有两个文件,一个是error_code.php,一个是要包含它的test_include.php文件。error_codes.php文件里定义了两个常量。代码如下:

      代码 2-68:error_code.php  - 定义用于错误标识的常量
      <?php
          $MY_OK = 0;
          $MY_ERROR = 1;
      ?>

在test_include.php中,使用include语句把error_codes.php文件包含进来。代码如下:

      代码 2-69:test_include.php- 使用include语句包含文件
      <?php
          include "error_codes.php";
          echo ('MY_OK的值等于$MY_OK\n");
      ?>

由于常量具有全局域属性,该脚本将打印为:MY_OK的值等于0。

也可以使用相对的路径或绝对的路径来引用文件。

        include $_SERVER["DOCUMENT_ROOT"] . "/myscript.php";

如果您使用的是虚拟主机(服务器中有多个网站在运行),上面的语句可能是行不通的,因为服务器的根目录可能不是网站使用的根目录,而要使用相对路径或绝对路径来做包含操作。

      require_once(dirname(_ _FILE_ _)."/myscript.php");

dirname()函数返回当前文件的目录名称,也可以利用自定义常量来定义引用路径。

        require_once(APP_PATH."/myscript.php");

即APP_PATH常量定义的是当前系统的绝对路径,这种方式值得推荐。

还可以使用相对的路径名。

        require_once("../myscript.php");

采用这种相对引用的方法,好处是不会因为系统环境的变化而出现找不到脚本包含的问题。其中的奥妙就是活用小圆点,它的数量和位置决定了相对路径指向的名称,如表2-18所示。

表2-18

另外,在PHP脚本中也要避免使用过多的完整路径。如尽量不要使用类似于/usr/web/domain.com/images/ad.gif这样的绝对路径,而要使用./images/ad.gif或../image/ad.gif这样的相对路径,这样避免在系统迁移时出现不必要的麻烦,其路径可以参考上面提到的include路径说明。

如果php.ini中的allow_url_fopen的开关是On(默认是关闭的),即:

        allow_url_include = On

我们还可以使用URL地址来进行文件包含。使用URL包含时,PHP会首先把这个文件的源代码下载到本地,然后包含到当前脚本,之前会再运行一次,这会耗费一些时间,比如:

        include("http://blog.sina.com.cn/index.php");

这个包含的URL地址必须返回一个正确的PHP脚本,且不是一个静态页面(不管它是不是PHP生成的)。如果包含的文件或URL地址不存在,包含文件时PHP会出现警告错误,但是不会影响后面脚本的执行。由于该操作较为耗时,因此该方法不建议经常使用。

也可以使用require语句来包含文件,方法与include语句相似,稍有不同的是:

使用require语句包含的文件中若有语法错误或不存在时会提示警告Fatal error,并立即终止程序的运行。而include只是显示Warning警告错误,然后继续执行该脚本后面的语句,除此之外,两者其他的功能是相同的。

2.18.2 include_once与require_once

这两个语句的模样很像include和require,它们与没有_once的语句的区别是什么呢?

这两个语句会“记住”什么文件已经被包含过了,有且仅会包含一次,如果尝试再次include_once/require_once相同的文件时,就会被PHP忽略。

由于使用require_once和include_once,PHP每次都会查询当前文件是否被包含,虽然这样会避免一些重复的引入文件错误,但也造成效率的低下。

在PHP 5后,特别是在5.2版本之后,PHP已经修改并优化了require和include的效率问题,如果PHP运行环境在5.2以上,使用require和include就可以了。

2.18.3 eval()

eval()函数有些类似于include语句,但是它可以动态执行PHP代码。

它可以使一个字符串变为PHP可执行的语句,比较有用的功能是,可以在运行时建立动态的代码或从一个外部数据源找回代码(如一个数据库中)并且执行,请见下例:

      代码 2-70:eval.php- 使用eval函数动态执行PHP语句
      <?php
          $str = '$var = 5;';
          eval($str);
          echo $var;
      ?>

该代码打印值为5,相当于执行$var=5这个表达式。

eval()还可以执行在字符串中的函数,包括PHP内置函数,在下面的例子里eval()函数的作用相当于执行exit()函数。

      代码 2-71:eval2.php- 使用eval函数执行内部函数
      <?php
          $str = 'exit()';
          eval($str);
          echo "程序执行到这里了.";
      ?>

该脚本将在echo语句前结束。

可以看出,eval()函数就像是PHP解析器,给它一个正确的PHP语句或表达式,它就能够解释执行。我们可以利用这一特性,比如将PHP代码保存到一个数据表中,使开发更灵活。

需要注意的是,在处理用户表单时不要直接使用eval()函数,它允许用户输入并执行任意的代码,会导致安全性问题。

2.18.4 中止脚本的执行:exit()与die()

不要小看这两个简单的函数,它会让PHP脚本更有效率。

在PHP手册及其他书籍中很少介绍它们的区别和具体用法,有鉴于此,有必要详细说明。

其实exit和dir这两个名字指向的是同一个函数,die()是exit()函数的别名。该函数只接受一个参数,可以是一个程序返回的数值或是一个字符串,也可以不输入参数,这个函数没有返回值。

当传递给exit()或die()函数的值为0时,意味着提前终止脚本的执行。请见下例:

      代码 2-72:exit.php使用exit()函数终止脚本执行
      <?php
        $var1 = 12345;
        echo $var1;
        exit(0);            //也可以使用exit()
        echo '控制脚本的运行';
      ?>

上面的脚本只会打印12345,它后面的语句不会被打印出来,因为遇到了exit()函数后,脚本已被提前终止执行。

当程序出错时,可以给它传递一个字符串,它会原样输出在系统终端上,通常情况下是使用die()这个名字,如下例:

      $fp = fopen("./readme.txt", "r") or die("不能打开该文件!");

在这种情况下,如果fopen打开文件出错,返回布尔值False,die()函数将立即终止脚本执行,并显示传递给它的字符串参数。

在实际开发过程中,除了使用系统提供的通用函数之外,还有一部分任务是编写适合自己项目业务逻辑的通用函数,在下一节中,将介绍函数的编写方法。

2.19 函数

函数是完成一个特定功能的代码集合,分为系统函数和用户自定义函数两种。在本章函数之前所说的函数全部指的是系统函数,也就是PHP本身提供的函数。

用户自定义函数是提供独立明确任务的程序,主要目的是组织与重用代码。

在一个开发团队中,可由几个项目成员分别编写不同的函数,然后在程序中调用函数,通过传递给函数一个或多个参数,函数通过参数来做不同的细节处理,达到增加代码重用性,避免重复开发,从而节省开发时间,提高效率。

如果把函数放在一个类中,那么它将被称为方法。接下来,我们就开始讨论自定义函数的编写。

2.19.1 函数的命名

PHP约定函数的命名比较宽松,既可以用小写字符命名,也可以大小写字符混合。为方便项目中的代码管理,我们在团队开发中要遵循统一的命名规则。

其他两个需要养成的好习惯是:

①函数名称应能很好地提示它的功能。

②函数名应包含表明所在模块的前缀。

假如我们在开发一个社区网站,有一个名为user的模块,它包含用户管理的函数,查看会员当前是否在线,使用user_is_online() 和userIsOnline() 这样的函数名称都是不错之选。

我们还可以把这个函数命名为is_online_checker()这样的名称,与前两个名称相比较,使用动词明显优于使用名词,因为函数总是要执行某个结果的。另外,命名也和程序员的英文水平有一定关系,对于国内的程序员来说,多使用看起来舒服的单词总是好的。

此外,在函数的命名与调用时,要注意以下三个方面:

➢ 在PHP中,函数名称不区分大小写。如getname()与getName()指向的是同一个函数,函数重名的情况出现时,程序将中止运行。

➢ 函数的参数没有限制,可以定义任意数量的参数个数。

➢ 函数的名称理论上可以用汉字,由于汉字是双字节字符,但为避免出现问题,请尽量不要使用汉字。

2.19.2 函数的结构

PHP函数的结构如下:

      function函数名称(参数1,参数2,参数3,…){
          //函数体
      }

通常情况下,是要让函数做一些有意义的工作,有时还要使用return语句把值返回给调用者,该值可以是标量变量、数组或布尔值。

调用函数的格式为:

函数名称(参数1,参数2,…,参数n)

我们来看下面的函数定义,代码如下:

      代码 2-73:define_function.php- 定义一个打印表格的函数
      <?php
        function print_table_row(){
          echo '<tr><td>表格的内容</td></tr>';
        }
        echo '<table border="1">';
        for ($i = 0; $i < 3; $i++){
          print_table_row();  //调用函数
        }
        echo '</table>';
      ?>

函数头的定义是这样的:

        function print_table_row()

因为没有使用参数,所以只有个括号即可。function关键字表示当前是一个函数,接着是函数的名字,这个名字使用动词表示,意思是打印表格的列,后面使用花括号包含的语句段,形成完整的函数体。

函数体中的变量称为局部变量,只在函数中有效。另外,当调用的语句与函数在同一个文件中时,函数在脚本中的先后顺序没有关系,如:

      代码 2-74:define_function2.php- 定义一个打印表格的函数
      <?php
        echo '<table border="1">';
        for ($i = 0; $i < 3; $i++){
          print_table_row();  //调用函数
        }
        echo '</table>';
        function print_table_row(){
          echo '<tr><td>表格的内容</td></tr>';
        }
      ?>

可以看到,这个脚本函数在尾部,调用前面的文件也一样没有问题。这个函数的功能是打印出3行的HTML表格。

2.19.3 从函数中返回值

有时在分支或循环语句中往往需要函数执行完后再返回原调用处,根据函数的返回值,以决定程序是继续执行应用逻辑处理还是出错处理。

在PHP中,使用return语句实现在函数中返回值。我们改写上面的函数例子,代码如下:

        代码 2-75:function_return.php- 在函数中使用return返回
        <?php
          function print_table_row(){
            return '<tr><td>表格内容</td></tr>';
          }
          echo '<table border="1">';
          for ($i = 0; $i < 5; $i++){
            echo print_table_row();
          }
          echo '</table>';
         ?>

该函数实现的功能与上例相同,但是函数中的echo换成了return语句返回值,注意,return语句只能用在函数中。

这样我们想把函数返回的内容保存到一个变量中,就可以写成:

        $row_info = print_table_row();

我们还可以让函数返回数组,如下例:

        function ret_array(){
          $result = array(12, 21, 36);
          return $result;    //使用return返回一个数组
        }
        $array = ret_array();

可以使用list函数显示返回的数组:

        list($var1,var2,var3) = $array;
        echo $var1,$var2,$var3;

这样将得到$var1、$var2、$var3共3个变量的值,然后显示出来。关于数组和list函数我们后面还会提到。

另外,我们也可以让函数返回一个布尔值,如下例:

        function ret_boolean(){
          return true;     //返回一个布尔值
        }
       $flag = ret_boolean();

因为是一个布尔值,我们需要用特殊的语句将其打印出来,使用如下语句:

        var_dump($flag);

将打印出来一个(boolean)ture,即布尔值True。

2.19.4 函数参数的传递

调用函数,采用值传递来控制函数的行为。在PHP中,对于函数的参数没有数目限制。尽管在函数定义上有一些不同的形式,但使用起来很灵活。下面我们就一起讨论函数参数传递的几种方式。

1.使用值传递

在函数中接收参数,只需要在函数头的括号加入相应的变量名。如下面的函数格式:

        function print_table_row($cell)

这个函数在名称上看是用来打印表格列,它接收1个参数,在函数体内能够访问$cell变量的内容。如果想为函数增加多个参数的传递时,这几个参数之间用括号分隔,格式如下:

        function print_table_row($bgcolor , $cell)

这个函数接收两个参数:$bgcolor和$cell,分别是表格的背景色和打印的列数。请看下面完整的函数定义:

        代码 2-76:function_parameters.php- 在函数中使用参数
        <?php
          function print_table_row($bgcolor, $cell)  {
            $cellstring = '';
            for ($i = 0; $i < $cells; $i++){
              $cellstring .= "<td> $i </td>";
            }
            $row = "<tr bgcolor=\"$bgcolor\">$cellstring</tr>";
            return $row;
          }
          echo '<table border="1">';
          echo print_table_row('gray', 3);
          echo print_table_row('red', 3);
          echo print_table_row('gold', 3);
          echo '</table>';
        ?>

函数会根据传递的背景色与列数打印出相应的表格。以上面这种方式传递参数,就称之为按值传递或传值(passing by value)。

有时候,在函数中按照某种逻辑处理时,当满足某个条件时,要让它从函数体中停止运行并跳出,可以使用空的return语句立即中止运行并返回到被调用处继续执行。请看下面的函数定义:

        代码 2-77:function_return_back.php– 在函数中使用空的return语句返回
        <?php
            function print_table_row($bgcolor, $cell) {
            $cellstring = '';
            if($bgcolor == 'red'){
              return;
            }
            for ($i = 0; $i < $cells; $i++){
              $cellstring .= "<td> $i </td>";
            }
            $row = "<tr bgcolor=\"$bgcolor\">$cellstring</tr>";
            return $row;
          }
          echo '<table border="1">';
          echo print_table_row('gray', 3);
          echo print_table_row('red', 3);
          echo print_table_row('gold', 3);
          echo '</table>';
        ?>

当第二次调用函数时,将不会显示表格内容,因为这时传递的参数是“red”,函数中判断了这个值是“red”,使用return语句提前返回了。

2.使用可选参数

函数的参数可以设置默认值,如果调用时不指定传递参数,函数将按该默认值进行处理,即参数传递是可选的。

下面的函数有两个参数,其中有一个参数是可选的,并且定义了为6的默认值,如下:

        代码 2-78:function_default_parameters.php- 函数参数使用默认值
        <?php
          function print_table_row($bgcolor, $cells = 6){
            $cellstring = '';
            for ($i = 0; $i < $cells; $i++)
            {
              $cellstring .= "<td> $i </td>";
            }
            $row = "<tr bgcolor=\"$bgcolor\">$cellstring</tr>";
            return $row;
          }
          echo '<table border="1">';
          echo print_table_row('gray');
          echo print_table_row('red');
          echo print_table_row('gold');
          echo '</table>';
        ?>

如果调用函数时没有使用第2个参数,函数内部会按默认值6来进行处理,即打印6列表格。

如果这个函数包括的参数都有默认值,这样在调用时不给任何参数,它会按默认的参数定义去完成任务。这样的函数定义是允许的:

        function print_table_row($bgcolor = 'gray', $cells = 6)

这样的函数允许我们在脚本中调用时不传递参数,调用时格式如下:

        print_table_row()

因为前面的函数中的两个参数都有默认值,因此这种调用仍然能够正常工作。下面的例子,功能是为用户生成新的密码,该函数就使用了两个默认参数。

        代码 2-79:function_general_password.php- 使用默认参数生成密码
        <?php
            function genPassword($min = 5,$max = 8) {
            $ValidChars = "abcdefghijklmnopqrstuvwxyz123456789";  //生成密码的候选字符
            $max_char = strlen($ValidChars) -1;
            $length = mt_rand($min,$max);  //使用mt_rand()随机选择
            $password = "";
            for ($i = 0; $i < $length; $i++) {
              $password .= $ValidChars[mt_rand(0,$max_char)];
            }
            return $password;
            }
            //没有指定任何参数,函数使用默认的参数执行,按$min的位数生成密码序列
            echo "新的密码 = " . genPassword() . "\n";
            //指定参数的调用
            echo "新的密码 = " . genPassword(4,10) . "\n";
        ?>

运行结果:

      新的密码 = sub7z新的密码 = nfa9om9iun

需要注意的是,在函数多参数定义时,如果只设置第1个参数有默认值,而第2个没有默认值,这种定义是错误的,如:

        function print_table_row($bgcolor = 'gray', $cells)

这样在调用时只写了一个参数或不写参数,PHP不知道哪个参数是被省略的,参数无法对应。

最后一点请大家注意的是,函数的参数不能是表达式,如下:

        function print_table_row($bgcolor = $gray, $cells = 3 + 3)

3.可变参数的函数

可变参数的函数,就是在编写函数时,可以根据传入不同的参数进行不同的处理,下面介绍三个在编写自定义函数时会用到的系统函数。

① func_num_args()

在PHP中,编写函数的参数没有数量限制。一般情况下,函数的参数应该控制在6个以内,如果参数太多,程序员需要检讨自己的编程思路,可能在设计时出现了问题。

在编写函数的时候,建议第一个要进行参数检查。其基本规则是:不信任任何传递的参数。实际上如果能做到这一点,软件的编程错误也就可以最大化地避免。

func_num_args()函数用于在用户编写的自定义函数中,获取目前传入了几个参数的数量,请看下面的代码:

        代码 2-80:func_num_args.php- 使用func_num_args函数
        <?php
          function getParaNum(){
            $arg_num = func_num_args();
            echo "传递的参数数量:".$arg_num; //显示传入的参数数量
          }
          getParaNum("abc",123,"cat");
        ?>

以上脚本执行结果为3,即传递了3个参数。

② func_get_arg()

另外func_get_arg(参数)函数,指定要存取哪个参数,第一个参数的值为0。可以结合func_num_args()函数自动获取传递的参数值,脚本如下:

        代码 2-81:func_num_args.php使用func_num_args函数
        <?php
          function getParaValue($a, $b) {
            for ($i = 0; $i < func_num_args(); ++$i) {
              $param = func_get_arg($i);
              echo "已取得参数值: $param.<br />";
            }
          }
          getParaValue(1,2,3,4,5,6,7,8);
        ?>

该脚本会打印出所有传递给函数的参数值。

我们接着使用一个结合func_num_args()和func_get_arg()两个函数来打印HTML表格的例子:

        代码2-82:function_params_plus.php– 改进的可变参数函数
        <?php
          function build_row($head, $cells){
            $params = func_num_args();
            $tag = $head ? 'th' : 'td';  //根据参数的布尔值,选择值为th列标题或td正常列
            if (($params -2) <= 0)  {   //如果当前参数不是偶数,会出现表格单元格不齐
                return "<tr><$tag colspan=\"$cells\"></$tag></tr>";  //使用colspan打印一行
            }
            $row = '<tr>';
            for ($i = 2; $i < $params; $i++){
                $row .= "<$tag>" . func_get_arg($i) . "</$tag>";
            }
            for ($k = $i -2; $k < $cells; $k++){ //在表格内容显示完毕后,显示一个空列
                $row .= "<$tag>&nbsp;</$tag>";
            }
            return "$row</tr>";
          }
          echo '<table border="1">';
          echo build_row(TRUE, 5, '编号', '玩具货号', '厂商', '简介');
          echo build_row(FALSE, 5, '00001', 'WG08030', '哆啦A梦12心情纪念版');
          echo build_row(FALSE, 5, '00002', '0G07012', '变形金刚','找寻博派与狂派的梦想');
          echo '</table>';
        ?>

这个函数的第1个参数用来指定表格头的显示方式,第2个参数是接收表格的列数,参数的数量由func_num_args来确定。当不同数量的参数传递时,会出现列数不齐的情况,HTML表中有一个叫colspan的属性,表示这个单元占几个单元格,函数中使用了colspan来做HTML表格单元格的对齐。

③ array func_get_args()

表示将这次传入的参数,以数组的方式返回。

        代码 2-83:func_get_args.php- 使用func_get_args()函数
        <?php
          function getParaArray($a ,$b) {
            $param = func_get_args();
            //$param内是一个一维数组,使用explode()函数分解显示
            $param = explode(", ",$param);
            echo "取得的参数为: $param.\n";
          }
          getParaArray(1,2,3,4,5,6,7,8);
        ?>

和前面脚本例子执行的结果相同,唯一不同的是,func_get_args()函数是把传递的参数归纳为一个数组。

4.使用引用传递参数

在前面的内容中,在函数中使用值传递参数时,每次只返回一个值,虽然大多数时候这个功能够用,但是若让函数能够通过参数来影响外部的值,这样对函数的处理会更加灵活。

我们再次探讨关于参数传递的原理。

在PHP中使用值传递为变量赋值,比如把一个变量的值分配给另一个变量时,其实是替代另一个变量的原值。变量相当于是计算机存储器的一个代号。如:

        $a = $b;

这一代码会把$b变量里的值替换为$a的值,之后也不影响变量$a,从优化存储数据来看,这并非最佳的方案,所以有了引用传递变量的赋值方式。

现代语言如C++和C#,提供了指针的概念——指向内存地址的变量,被称为指针。C++的指针在内部,对于用户来说并不可见,C++指针的特点是直接访问内容,速度快。

PHP的指针和这些语言一样,可以用一个变量名称把工作地址与原始存储位置建立一个关系。

如果我们在一个函数的参数上加入引用,这表示当函数对该内部变量的值进行修改时,同时也能够反应到函数外部,你只要在对应的参数前面加上“&”就可以。请见如下脚本:

        代码 2-84:function_reference.php- 在函数中实现引用
        <?php
          function build_row(&$text){
              $text = "<tr><td>$text</td></tr>";
          }
          echo '<table border="1">';
          $t = '测试用数据';
          build_row($t);
          echo $t;
          echo $t;
          echo $t;
          echo '</table>';
      ?>

该脚本执行的结果如图2-17所示。

图2-17

使用引用的特征是函数的参数有没有“&”符号,该定义确定是否采用引用传递,正是这个符号的存在使得它能够“感受”到内部函数对它的修改.所以按值和按引用的差别也就在这里了。

在PHP 5中,我们也可以在调用函数时使用“&”符号引用,如下:

        build_row(&$t);

关于函数变量的问题,还有其他特殊相关的功能。我们在下两节的内容中介绍。

2.19.5 函数与全局变量

作用域,在PHP中称为“Scope”,表示标记或范围之意。它指定了在PHP代码中哪些地方可以使用函数中的变量,它们能在程序中的什么地方被调用。请看下面例子:

        代码2-85: function_variable_scope.php- 函数中局部变量的作用域
        <?php
            function func (){
              $var = 2;
            }
            $var = 1;
            func();
            echo $var;
        ?>

首先在函数外部,我们将$var变量的值置为1,当函数func被调用时,func内部将$var变量的值置为2,但是函数中的$var属于局部变量,不会影响到外部,因此该脚本的执行结果仍是1。

可以在程序中使用global关键字或者使用$GLOBAL[]数组来使一个变量在整个应用程序内具备全局属性。下面将上面的程序稍修改一下。

        代码2-86:function_global.php- 在函数中声明全局变量
        <?php
            function func (){
            $GLOBALS["var"] = 2;        //或使用global $var;
            $var = 2;
            }
            $var = 1;
            func();
            print $var;
        ?>

此时,$var的值就被修改成2了,因为$var已经声明为全局变量,当有一处对$var的值做了修改时,别的地方引用该变量时也会随着同步改变。

我们需要了解以下变量作用域的几点注意事项:

➢ 在函数内部声明的变量作用域从声明开始一直到函数的尾部始终有效。

➢ 在函数外部声明的变量作用域是从声明开始到声明所在的PHP文件末尾一直有效。

➢ 使用require与include包含不会影响作用域。

➢ 如果变量在函数内部,只在函数作用域有效;如果它不在函数内部,具有全局作用域。

➢ 使用关键字global或$GLOBALS[]数组可以手动指定一个函数中使用的变量是全局变量。

➢ 通过参数列表传递给函数的变量,对于函数来说是局部变量,除非在传递时带有&引用。

➢ 可以使用unset命令手动删除一个变量,该变量在其作用域内也同时被销毁。

2.19.6 函数的静态变量

在函数中定义的局部变量,在变量调用结束后,这个变量的值不能再保留。如果在函数中使用静态变量,当函数执行结束后,第二次调用时,在函数中也可以取得原来用过的值,也就是说系统保存了原来执行后静态变量结果。

在函数中声明静态变量,在局部变量前面加入static关键字声明即可,如:

        static $variable;

下面我们来看具体脚本中函数静态变量的定义:

        代码2-87:function_static.php定义了静态变量的函数
        <?php
            function static_ex( $num ){
              $a = 10;
              static $b = 10;
              echo $num . "<br />";
              echo $a++ . "<br />";
              echo $b++ . "<br />";
            }
            static_ex( 1 );
            static_ex( 2 );
            static_ex( 3 );
        ?>

我们执行了三次static_ex()函数,执行结果如下:

      1
      10
      10
      2
      10
      11
      3
      10
      12

结果的前3行是第1次调用函数的执行结果,函数执行的过程是首先显示传递进来的值是1,然后显示$a变量递增后的值,因为是加号在后,属于先显示后计算,这里显示的内容是1;接着显示$b变量,与$a变量一样,它的显示也是10,但需要注意的是,$b变量已经声明为静态变量。

接着再看第2次调用该函数的结果,也就是结果的第4~6行。第2次传递给函数的值是2,所以显示为2,然后显示$a变量递增后的值,结果为10;接着显示$b变量递增的值,而$b声明为静态变量,这里会取得上一次调用的结果,因此显示的值是11。

第3次调用函数时,静态变量$b的内容是12。其他变量因为是局部变量,除传递的参数是13外,其他结果与第1次和第2次函数调用的结果相同。

我们可以利用静态变量的这个特性做一个计数器,比如在输出一个HTML表格时自动增长编号,脚本的函数定义如下:

          代码 2-88:function_static_buildrow.php– 使用静态变量输出表格行编号
          <?php
            function build_row($brow_number, $bhead, $bcells){
              static $rowNumber = 1;
              $cells = $bcells;
              $Params = func_num_args();
              $sTag = $bhead ? 'th' : 'td'; //根据布尔值显示表格的行属性
              if (($Params -2) <= 0){     //如果参数是偶数时,正常显示表格一行
                  if ($bbrow_number){
                        $cells++;
                  }
                  return "<tr><$sTag colspan=\"$cells\"></$sTag></tr>\n";
              }
              $row = '<tr>';
              if ($brow_number){
                  $row .= "<td>$rowNumber</td>\n";   //显示表格的编号
                  $rowNumber++;
              }
              for ($i = 2; $i < $Params; $i++) {
              $row .= "<$sTag>" . func_get_arg($i) . "</$sTag>\n";
              }
              return "$row</tr>";
            }
            echo '<table border="1">';
            echo build_row(FALSE, FALSE, '编号', '货号','产地', '厂商', '简介');
            echo build_row(TRUE, FALSE, '12683','广东','多拉A梦', '哆啦A梦12心情纪念版');
            echo build_row(TRUE, FALSE, '89315','深圳','变形金刚', '找回年轻时的偶像,找寻博派与狂派的梦
          想');
            echo build_row(TRUE, FALSE, '59218','北京','史奴比', '漫画家查尔斯·舒尔茨1950年起连载的漫画
          作品');
          echo '</table>';
          ?>

该脚本执行后将显示一个有编号的表格,如下:

2.19.7 函数中使用常量

常量在脚本中具有全局属性,那么在函数中,常量也可以直接访问,如下面的例子:

      代码2-89:function_global_constant.php– 使用常量的函数
      <?php
          define('CELLS', 6);  //定义常量
          function print_table_row($bgcolor){
            $cells = CELLS;
            $cellstring = '';
            for ($i = 0; $i < $cells; $i++){
              $cellstring .= "<td> $i </td>";
            }
            $row = "<tr bgcolor=\"$bgcolor\">$cellstring</tr>";
            return $row;
          }
          echo '<table border="1">';
          echo print_table_row('gray');
          echo print_table_row('red');
          echo print_table_row('gold');
          echo '</table>';
      ?>

在脚本的函数中直接使用了CELLS常量的值:

          $cells = CELLS;

把它的值赋给$cells的局部变量后,根据它的值打印表格的列数。

2.19.8 递归函数

对自身调用的函数被称为递归函数。

递归是计算机科学技术中的一部分,在日常开发中有许多应用。比如列出一个目录的内容,目录中还包含多个子目录,子目录还包括子目录,只使用一个循环难于实现,因此需要使用递归功能来实现。

在PHP中,使用递归函数调用的层数也不是没有限制的。在PHP 4中,递归层数是限制在254以下,PHP 5因为是动态分配内存,理论上可以支持6000个递归调用,这个定义并不精确,取决于内存情况与操作系统。

我们应该考虑的,几百个以上的函数递归都属正常,如果需要更多的递归调用,需要重新思考自己的想法,很可能是设计错误。

来看下面实现递归函数的脚本代码:

        代码2-90:function_recursive.php- 递归函数的定义
        <?php
          function summation ($count) {
              if ($count != 0){
                return $count + summation($count-1);  //递归调用本身
              }
          }
          $sum = summation(10);
          echo "求和 = $sum";
        ?>

该脚本执行的是从10加1,结果是55。

“迭代者为人,递归者为神”,当然“神”的意思不是说神写的代码,是只有神能看懂这样的代码。所以递归用太多会给日后维护的人造成困惑,所以程序员在使用递归时,一定不要超过PHP的递归层数限制。

2.19.9 解决函数重名的方法

在团队协同开发的项目中,有时候难免会存在函数命名重复的情况,可以使用function_exists()函数来检测用户自定义的函数名称是否已经存在。

        代码2-91:function_check_exists.php- 检测函数是否已定义
        <?php
            if(!function_exists("showMessage") {
            function showMessage($message){
                echo "$message";
            }
            showMessage('Test Message');
            }
        ?>

还可以使用create_function()快速创建一个函数,这个函数名称由PHP动态产生,因而可以避免与代码中的函数名产生冲突。

        代码2-92:create_temp_function.php- 动态创建一个函数
        <?php
          $func_name = create_function('$messager','echo "你好,{$message}";' );
          echo $func_name;
        ?>

上例中会显示这个临时创建的函数名称。

函数是从代码重用的角度考虑的,当实现了很多个函数时,就需要把同类别的函数组织到一起,然后保存到一个文件中,如通用的函数库放在common.inc.php中,这样以后在类似的项目中,直接require(包含)进来就可以复用这些函数。

2.20 可变变量

在PHP中,还允许程序员动态地把一个变量的内容(值)作为另一个变量的名字,称为可变变量。

来看下面代码:

        代码2-93:variable_variable.php使用可变变量
        <?php
          $var_name = 'php5';
          $$var_name = 'PHP5Web开发详解';  //这是一个可变变量,相当于$php5= 'PHP5 Web开发详解'
          echo $php5;                    //显示$PHP5变量的内容
        ?>

在该脚本中我们开始创建一个变量$var_name,它的内容是一个字符串“php5”,接着我们用$$var_name这种特殊的语法来声明一个可变变量,从而将创建一个新变量$php5,因此会正常打印出一个值。

由于在脚本中变量值是不确定的,因此在使用变量的变量来创建变量名时并不一定要遵循变量名命名规则,请看下面代码:

        代码2-94:variable_variable2.php- 使用可变变量
        <?php
          $name = '123';  // 正常赋值
          $$name = '456'; // 使用变量的变量进行赋值,相当于$123=456,在正常脚本中,$123是一个非法命名
          echo ${'123'};  //使用花括号来显示
       ?>

在可变变量中,如果变量名不合法,我们使用花括号来显示,该脚本执行的结果显示的是变量$123的值-“456”。

和变量中的变量相似,我们也可以利用该方法动态的引用函数,脚本代码如下:

        代码2-95:variable_variable3.php- 使用可变变量动态引用函数
        <?php
          function myFunc($str) {
              echo $str;
          }
          $f = 'myFunc';
          $f("TEST"); // 将参数TEST传递给函数myFunc并输出;
        ?>

可变变量是一个强大的工具,也是PHP独有的功能。但我们在使用中也要小心,过多的使用会使PHP代码可读性不好,使程序变得不那么容易理解,并且可能会引发一些安全性问题。

2.21 字符串操作

2.21.1 手动转义字符串数据

当使用字符串时,很可能该字符串中也有单引号(')和双引号(")等与PHP脚本混淆的字符,因此必须要用转义语句告诉PHP引擎,有些字符无需用它来解释。只需在要转义的字符前加 “\”左斜杠即可实现转义,请看下面的例子:

        代码2-96:esape.php  - 使用转义操作符
        <?php
            $name = "Garfield";
            //字符串定界符是单引号,文本内容中字符有单引号,需做转义操作
            $str1   = 'The '.$name.' cat\'s is pretty \n';
            echo $str1;
            //字符串定界符是双引号,里面的单引号字符不需要转义
            $str2 = "This is one line.\nAnd this's another line.";
            echo $str2;
            //转义变量名
            $var = 123;
            $str3 = "The \$var is $var";
            echo $str3;
        ?>

执行结果如下:

        The Garfield cat's is pretty
        The Garfield cat's is pretty
        The $var is 123

因此在开发时,尽量使用单引号作为字符串定界符,在执行速度上,由于不进行变量解释,单引号的速度要略快于双引号。

2.21.2 自动转义字符串数据

1.addslashes()

格式:addslashes(string)

addslashes()函数的作用是为字符串里面的部分字符添加反斜线转义字符,addslashes()函数只为4 个字符添加转义,包括:单引号“’”,双引号“””,反斜线“\”和NULL(“\0”)。

该函数用于在数据库操作时,将sql语句中有可能与SQL冲突的字符串进行转义或过滤一些错误、恶意注入操作等。脚本如下:

        代码2-97:add_slashes.php使用addslashes函数自动转义
        <?php
          $str = "[sql]Who's Raymond?";
          echo $str . " This is not safe in a database query.<br />";
          echo addslashes($str) . " This is safe in a database query.";
        ?>

注意:PHP会对$_GET[],$_POST[],$_COOKE[],$_REQUEST[]与表单相关的数组自动增加转义功能,因此在使用addslashes()函数对这几个超级变量数组内容进行转义操作时,会导致错误。

另外还有一个stripslashes()函数,它是addslashes()的反操作,即将转义的字符串转为正常的字符串。如果使用了addslashes()函数将数据插入到数据表中,在以后作数据查询(如SELECT)操作时,请记得用stripslashes()函数将其还原为正常的数据。

2.addcslashes ()

addcslashes()函数类似于C语言中使用反斜线转义字符串中的字符。

格式:addcslashes(<string>,characters)

其中,characters是指定需要通过addcslashes()函数执行转义操作的字符串中的字符。

注意:需要特别注意addcslashes()函数中关于o,r,n和t的用法,因为在PHP中,\o,\r,\n和\t是预先定义的转义序列。

在下面的例子中,利用该函数给字符串中指定的字符加上转义字符反斜线“\”。

        代码2-98:addcslashes.php- 使用addcslashes函数指定转义
        <?php
          $str = "Hello Friend,Never give up!";
          echo $str."<br />";
          //指定在g字母前加转义字符
          echo addcslashes($str,'g')."<br />";
          //指定在n字符前加转义字符
          echo addcslashes($str,'n')."<br />";
        ?>

上述代码将输出的结果如下:

        Hi Friend,Never give up
        Hi Friend,Never \give up
        Hi Frie\nd,Never give up

再看一个例子:给字符串中指定的一组字符串加上反斜线“\”。

        代码2-99:addcslashes2.php- 使用addcslashes()函数指定转义
        <?php
          $str = "Hi Friend,Never give up.";
          echo $str."<br />";
          echo addcslashes($str,'A..Z')."<br />";
          echo addcslashes($str,'a..z')."<br />";
          echo addcslashes($str,'a..g');
        ?>

输出结果如下:

        Hi Friend,Never give up.
        \Hi \Friend,\Never give up.
        H\i F\r\i\e\n\d,N\e\v\e\r \g\i\v\e \u\p.
        Hi Fri\en\d,N\ev\er \giv\e up.

这几个函数会在表单和内容处理中经常用到。

2.21.3 数值转换字符串

chr()函数

格式:chr($var)

chr()函数是根据$var中的ASCII值返回相对应的字符,因此$var变量必须是一个合法的ASCII码值。如:

        代码2-100:chr.php- 使用chr函数
        <?php
            $var = 98
            echo chr($var);
        ?>

该代码将打印小写的字母b。

ord()函数

格式:ord($string)

ord()函数返回$string字符串首字符的ASCII码值。这个函数是补充于chr()函数的。

请看下面的脚本例子:

        代码2-101:ord.php- 使用ord()函数取得字符ascii码
        <?php
            $str = "hello";
            if (ord($str) == 10) {
            echo "\$str第一个字符的ASCII码是10.\n";
            }else{
            echo "\$str第一个字符的ASCII码是".ord($str);
            }
        ?>

因为h所代表的ASCII码不是10,它将显示:

      第一个字符的ASCII码是104
2.21.4 字符串序列化

serialize()函数用于将数据集转换为可存储在数据库或会话记录中的字符串(如SESSION或cookie的字符串)。如下例:

        代码2-102:serialize.php– 使用serialize()函数对数据进行序列化
        <?php
            $s = 'PHP';
            echo serialize($s);
        ?>

脚本执行结果如下:

        s:3:"PHP";

可见它已经被PHP序列化,下面对序列化后的结果说明如下:

's'——表示是一个字符串,该类型属串行化的变量。

'3'——表示长度为3。

'PHP'——表示值。

';'——是分隔符。

再看一个稍复杂点的例子,对一个数组进行序列化操作。

        代码2-103:array_serialize.php- 对数组数据序列化
        <?php
            $ar = array('PHP','key'=>'serialize');
            echo serialize($ar);
        ?>

打印结果如下:

        a:2:{i:0;s:3:"PHP";s:3:"key";s:9:"serialize";}

下面将数组被序列化后的字符串的含义说明如下:

'a' 表示为一个数组;

'i' 表示为一个整数(integer);

'i:0' 是该数组第一个元素的索引键值(integer key),不需要分配一个键(key),PHP从一个字符串中分配一个字符。

unserialize()函数是反序列化,它会把序列化的数据还原为正常的数据。我们看一个更加详细的例子,从中可以深入观察被序列化后的结果。

        代码2-104:serialize_plus.php- 使用serialize()和unserialize()函数对数据序列化和反序列化
        <?php
            $arry = Array("cats","drop","fur","everywhere");  //创建一个数组
            echo "原数组内容:<pre>";
            print_r($arry);
            echo "</pre>序列化后的数组:<br />";
            $string2 = serialize($arry);                    //序列化
            print_r($string2);
            echo "</pre>反序列化后的数组:<br /><pre>";
            $arry2 = unserialize($string2);                 //反序列化
            print_r($arry2);
            echo "</pre>";
        ?>

该脚本的输出结果如下:

        原数组内容:
        Array
        (
            [0] => cats
            [1] => drop
            [2] => fur
            [3] => everywhere
        )
        序列化后的数组:
        a:4:{i:0;s:4:"cats";i:1;s:4:"drop";i:2;s:3:"fur";i:3;s:10:"everywhere";}
        反序列化后的数组:
        Array
        (
            [0] => cats
            [1] => drop
            [2] => fur
            [3] => everywhere
        )
2.21.5 清理字符串中的空格

有时候我们需要把字符串中一些不必要的空格和格式控制符去掉,这在一些严格的数据检查中很有用。比如检查表单传递的内容,要使用类似于下面的语句:

        $Name   = trim($_POST["Name"];
        $Email  = trim($_POST["Email"]);
        $Feedback = trim($Feedback);

trim()函数的作用就是去除变量中不必要的空格、换行符(\n,\r和\r\n)、水平和垂直制表符(\t)以及字符串结束符(\0)。

和trim()类似的兄弟函数还有:

ltrim()——去掉字符串变量左侧的空格和格式控制符;

rtrim()——去掉字符串变量右侧的空格和格式控制符。

有关这三个函数的使用方法在表单一章中还会有更详细的使用介绍。

2.21.6 格式化字符串

1.nl2br()与wordwrap()

这两个函数提供的都是文本或字符串换行功能。

nl2br()是将字符串中的\n换行符转换为html中的<br />标签。经常用于用户提交表单文本内容,经nl2br()处理换行后插入到数据表中,或从数据表中提取后,用它转换显示一个标准的HTML换行的内容。

wordwrap函数用于指定从某列字符进行强制换行。请看下面的代码应用实例:

        代码2-105:wordwrap_nl2br.php- 使用wordwrap()和nl2br()函数
        <?php
            $string = "Hello\nWorld\n\nHow are you?";
            echo nl2br($string) . "<br />";         //将\n转换为浏览器识别的<br />换行符
            $textblock = "See spot run,run spot run.  See spot roll,run spot roll!";
            echo wordwrap($textblock,20,"<br />"); //在一行的第20个字符处强制换行
        ?>

该脚本在浏览器上的显示结果如下:

        Hello
        World
        How are you?
        See spot run,run
        spot run. See spot
        roll,run spot roll!

2.改变字符串的大小写

有4个函数可以重新格式化字符串中的大小写。请看下面的代码示例:

        代码2-106:change_case.php  - 改变英文字符的大小写
        <?php
            $astring = "hello world";
            echo strtolower($astring);  //将字符串全部转为小写字符
            echo strtoupper($astring);  //将字符串全部转为大写字符,显示为HELLO WORLD
            echo ucfirst($astring); //转换首字符大写,显示为Hello world
            echo ucwords($astring); //转换单词首字符大写,显示为Hello World
        ?>

这4个函数分别是strtolower()、strtoupper()、ucfirst()和ucwords(),它们的参数均是一个字符串变量,然后返回它们的处理结果,在处理用户数据时会用到。

2.21.7 字符串切分

格式:array explode ( string $delimiter, string $string[, int $limit] )

explode()函数用于切分一个有明显标识的字符串,切分后返回一个索引数组。请看下面代码:

        代码2-107:explode.php  - 使用explode对字符串进行切分
        <?php
            $email = "webmaster@php.net";
            $mail_array = explode("@",$email); //以@为标识进行切分
            print_r($mail_array);              //使用print_r函数打印数组
            $url_array = explode("/", $_SERVER['REQUEST_URI']);
            print_r($url_array);
        ?>

这样会在$emai_array数组中产生两列数据,分别为:

        Array
        (
            [0] => webmaster
            [1] => php.net
        )

$url_array数组是从当前URI(即主机名后面的路径和字符串)地址中分解数据,比如当前访问该脚本的地址为:http://localhost/base/explode_str.php,那么该数组中将包含如下内容:

        Array
        (
            [0] =>
            [1] => base
            [2] => explode_str.php
        )

我们在脚本中使用了print_r()函数来打印数组的内容。另外值得一提的是,explode()函数还有一个别名join(),如果在看其他人写的源代码中有用这个名字,请不要陌生或感到奇怪,我们在开发中任选其一即可。

2.21.8 字符串截取

格式:string substr ( string $string, int $start [, int $length])

substr()函数非常实用,截取字符串是它的长项,来看下面的例子:

        代码2-108:substr.php  - 使用substr()函数截取字符串
        <?php
            $test = "Your programing ability is excellent ";
            echo substr($test,1);      //将得到"our programing ability is excellent"
            echo substr($test,-9);     //得到"excelent"
            echo substr($test,0,4);    //将得到"Your"
            //将从第4个字符开始截取到倒数第13个字符,即"programing ability "
            echo substr($test,4,-13);
        ?>

再看一个实现应用的例子,比如在网站上一篇文章的标题文字太长,用substr()函数截断,后面的部分以省略号代替。

        代码2-109:substr_format.php- 用substr()函数截取字符串并格式化
        <?php
            $theclientstext = "Hello lilia,how are you today? I am fine!";
            if (strlen ($theclientstext) >= 30){
              echo substr ($theclientstext,0,29)."...";
            } else {
              echo $theclientstext;
            }
        ?>

显示结果如下:

      Hello lilia,how are you today? I a...

substr()函数对英文的处理是没有任何问题的,而事实上经常处理的是汉字,需要更换使用iconv_substr()函数或mbstring扩展库中的mb_substr()函数。要做的就是把strlen改为iconv_substr或mb_substr,这样就可以方便的处理汉字或其他双字节字符。有关这两个函数库的内容,在本书第7章会作详细的讨论。

2.21.9 计算字符串的长度

格式:strlen<$string>/mb_strlen(<$string>);

使用该函数计算一个字符串的长度,请看下面例子:

        代码2-110:strlen.php- 用strlen计算字符串长度
        <?php
            echo strlen("China is great");      //注意空格也算为一个字符,返回14
            echo strlen("伟大的中国");    //返回10,一个汉字占用两个ASCII字符长度
        ?>

可以利用strlen()函数与常量配合来检查用户输入数据的长度是否足够,类似于如下代码:

      代码2-108:strlen_constant.php - strlen()与常量结合使用
      <?php
          //定义一个常量,用以规定输入文本时的最大长度
          define ("MAXLENGTH", 10);
          if(strlen("Hello World!") > MAXLENGTH){
            echo "您已经超过了系统定义的长度: " . MAXLENGTH . " 请重新输入";
          } else {
            //此处可为插入数据到数据库的语句块
            echo "输入数据正常";
        }
      ?>
2.21.10 字符串单词统计

格式:str_word_count($string)

这个函数功能是统计一段文字中有多少个英语单词。请见下面实例:

        代码2-111:str_word_count.php使用str_word_count()函数计算单词数
        <?php
            $text = <<<text
            测试:programing is art! wahaha
        text;
            $words = str_word_count($text);
            echo "单词总数为: $words";
        ?>

输出结果是:

      单词总数为: 4

由此可以看见,在字符串加入wahaha这类的“单词”,并加入空格作为区分,它也会当做一个词汇,也就是这说,这个函数就是根据文本中是否为英文字符加空格进行识别,并计算单词数量。

还可以利用该函数与数组统计函数array_count_values()结合使用,可以对文本中单词的出现次数进行统计,脚本代码如下:

        代码2-112:str_word_count_plus.php– 统计单词出现的次数
        <?php
            $text = <<< text
            To be pure PHP developer,PHP is simple,
            PHP is good web script.
            building our web site.
        text;
            $words = str_word_count($text, 2);
            //使用array_count_values对$words作统计后生成一个新数组
            $count_array = array_count_values($words);
            print_r($count_array);
        ?>

该脚本输出结果如下:

        Array
        (
            [To] => 1
            [be] => 1
            [pure] => 1
            [PHP] => 3
            [developer] => 1
            [is] => 2
            [simple] => 1
            [good] => 1
            [web] => 2
            [script] => 1
            [building] => 1
            [our] => 1
            [site] => 1
        )

注意:该函数不能对汉字等双字节字符进行统计。

2.21.11 字符串查找

1.子串查找strstr()

格式:strstr($string , "keyword");

在一个字符串中查找指定的关键字符,可以使用strstr()函数来完成该功能。请看下面的代码例子:

        代码2-113:strstr.php- 使用strstr()函数检查字符串是否有指定的字符
        <?php
            $string = "this is a demo string";
            if(strstr($string,"demo")){
              echo "有该子串";
            } else {
              echo "没有该子串";
            }
        ?>

strstr()函数返回的是一个布尔值,因为字符串$string有“demo”字样,该脚本将输出如下结果:

        有该子串

strstr()函数可以应用在会员名检测,垃圾文字检测等方面。另外,还有一个strrchr()函数与strstr()函数功能很相似,只不过strrchr()函数是从字符串后序的位置开始查找。

2.查找子串的位置strpos()

格式:int strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )

和strstr()函数功能相似,只是返回的不是一个字符串,而是子串所出现的位置号。请看下面的脚本例子:

        代码2-114:strpos_checkbrowser.php- 使用strpos检测IE浏览器的版本号
        <?php
          function checkBrowser(){
            if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
              if(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6')){
                return "MSIE 6";
              }elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 7')){
                return "MSIE 7";
              }elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 8')){
                return "MSIE 8";
              }
            }elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox')){
                return "Firefox";
            }
          }
          echo checkBrowser();
        ?>

这里checkBrowser()自定义函数使用strpos()函数来检测$_SERVER['HTTP_USER_AGENT']超级全局数组内容,根据返回内容来判断当前浏览器的类型和版本号。

还可以指定strpos()函数从字符串中的第几个字符开始查找,代码格式如下:

      //从第5个位置开始查找
      echo strpos($string, "demo", 5);

另外,与strpos()类似的函数还有mb_strpos()函数,用于双字节字符的处理,如汉字、日文的查找等。

3.字符串重复str_repeat()

格式:string str_repeat ( string $input , int $multiplier)

该函数用于重复显示一个字符或字符串。显示一个显示10次“PHP 5”的字符串的代码如下:

        <?php
            $repeat_str = str_repeat("PHP5",10);
        ?>

4.查找子串出现的次数substr_count()

格式:int substr_count($string,$keyword)

其中$string为一个要处理的源字符串,$keyword是在该字符串出现的关键字,使用该函数可以对一个字符串中某个关键字出现几次进行统计。请见下面例子:

        代码2-115:str_repeat.php- 使用str_repeat()和subsr_cont()函数
        <?php
            $repeat_str = str_repeat("PHP5",10);
            $counter = substr_count($repeat_str,"PHP5");
            echo "字符串中PHP5字符一共出现 " . $counter . " 次.";
        ?>

结合上例,对$repeat_str中的PHP 5单词根据出现次数统计,结果为:

        字符串中PHP 5字符一共出现10次.
2.21.12 字符串处理

1.字符串替换str_replace()

格式:mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

str_replace()函数需要3个参数,第1个参数是要查找的目标关键字;第2个参数是要替换的字符,也可以是空白字符,这样相当于替换掉目标关键字;第3个参数是搜索的目标文本。

该函数会在一些论坛以及留言板等系统开发里经常使用。请看下面脚本:

        代码2-116:str_replace.php- 使用str_replace函数进行文字替换
        <?php
          $text_1 = "Welcome to Beijing";
          $text_2 = "China";
          $text_3 = str_replace("Beijing", $text_2, $text_1);
          echo $text_3;
        ?>

执行后是被替换的字符串:“Welcome to China”,str_replace函数也可以进行汉字替换。

str_ireplace()函数是忽略大小写的str_replace()函数。

值得一提的是,要使用更精确的搜索替换功能,需要结合正则表达式来进行,每个软件中都会应用正则表达式。这些内容将在第3章“正则表达式”中详细介绍。

2.字符串翻转strrev()

strrev()函数用于将字符串翻转。我们来做一个比较有意思的程序,比如把“abcdefg12345”转为“54321gfedcba”,代码如下:

        代码2-117:strrev.php- 实现字符串翻转
        <?php
            $string = "abcdefg12345";
            echo strrev($string);
        ?>

输出结果为:

        54321gfedcba

可以看到,使用strrev()函数实现简直易如反掌。还有其他实现方式,在您读完本章内容后,可以结合循环语句自己编写一个实现同样功能的函数。

3.转换HTML实体

在HTML中,有些字符有一些特殊的含义,比如小于号“<”是定义一个HTML标签的开始。假如想要浏览器显示这些字符的话,需在HTML代码中插入字符实体。

一个字符实体(entities)包括三个部分:一个与符号(&),一个实体名或者一个实体号,最后是一个分号(;),比如要在HTML文档中显示一个小于号,要这样写:“&lt;”,半角空格为“&nbsp;”,&符号为“&amp;”等。

在PHP中,htmlspecialchars()和htmlentities()函数就是专门用来将字符转换为HTML实体的,一方面是为了页面显示安全,另一方面也是为PHP代码不易出错。

格式:string htmlspecialchars ( string $string [, int $quote_style = ENT_COMPAT [, string $charset [, bool $double_encode = true ]]] )

该函数会把“<”、“>”、“&”和“”这4 个字符分别转换为“&lt;”、“&gt;”、“&amp;”和“&quot;”的HTML实体字符。详见下面的脚本样例:

        代码2-118:htmlspecialchars.php- 使用htmlspecialchars()函数转换字符实体
        <?php
            $str = '<a href="http://www.google.cn" title="Google China">Google中国</a>';
            echo htmlspecialchars($str);
        ?>

上例在浏览器上显示的结果为:

        <a href="http://www.google.cn" title="Google China">Google中国</a>

从外表上看好像是一样的,可以通过浏览器查看源代码的方式,就知道浏览器为什么不解释它,HTML的源代码如下:

        &lt;a href=&quot;http://www.google.cn&quot; title=&quot;Google China&quot;&gt;Google中
        国&lt;/a&gt;

因为字符串已经被转换成HTML实体字符,不是标准的HTML标签,所以浏览器是按普通文本原样显示的。

htmlspecialchars()函数的第二个可选参数是选择引号的转换模式,可以选择以下三个常量。

ENT_COMPAT:表示转换双引号但是保留单引号;

ENT_QUOTES:表示同时转换双引号和单引号;

ENT_NOQUOTES:表示两个都不转换,默认值为ENT_COMPAT。

请看下面脚本:

        代码2-119:htmlspecialchars_custom.php- 使用htmlspecialchar()手动转义
        <?php
            $str = '<a href="http://www.google.cn">Google中国</a>';
            echo htmlspecialchars($str, ENT_COMPAT);
            echo "<br />";
            echo htmlspecialchars($str, ENT_QUOTES);
            echo "<br />";
            echo htmlspecialchars($str, ENT_NOQUOTES);
            echo htmlentities($str,ENT_COMPAT,"UTF-8");
        ?>

htmlspecialchars()的第三个可选参数表示所转换字符的编码集。它支持以下字符集:

➢ ISO8859-1也称Latin-1,西欧字符集;

➢ ISO8859-15也称Latin-9,西欧字符集。

增加了Latin-1(ISO-8859-1)中缺少的欧元符号、法国及芬兰字母。

➢ UTF-8也称ASCII兼容多字节,8位Unicode编码;

➢ cp866 DOS系统特有的Cyrillic字母字符集;

➢ cp1251也称Windows-1251,win-1251,1251,Windows-特有的Cyrillic字母字符集;

➢ cp1252也称Windows-1252,1252,Windows对于西欧特有的字符集;

➢ KOI8-R也称koi8-ru, koi8r,俄文字符集;

➢ BIG5也称cp950,繁体中文,主要用于中国台湾地区;

➢ GB2312也称cp936,简体中文,中国国家标准字符集,主要用于中国大陆和新加坡地区;

➢ BIG5-HKSCS繁体中文,是Big5编码的延伸,主要用于中国香港特别行政区;

➢ Shift_JIS也称SJIS,932,日文;

➢ EUC-JP也称EUCJP,日文另一种字符集。

格式:string htmlentities ( string $string [, int $quote_style = ENT_COMPAT [, string $charset [, bool$double_encode = true ]]] )

htmlentities()函数的用法和htmlspecialchars()函数类似,但是htmlspecialchars()函数可以转义更多的HTML里面的字符,所以使用时要注意加上第三个参数。

html_entity_decode()函数是htmlentities()的反函数,即把HTML实体转换为正常的字符,参数与htmlentities()函数相同,使用时也要注意用到第三个参数,请见下面的脚本例子:

        代码2-120:htmlentities.php- 使用htmlentities和html_entity_decode函数
        <?php
            $str = "<a href='http://www.google.cn'>Google中国</a>";
            echo htmlentities($str,ENT_COMPAT,"iso-8859-1");
            echo "<br />";
            //按指定的编码还原被转换的HTML实体字符
            echo
        html_entity_decode(htmlentities($str,ENT_COMPAT,"iso-8859-1"),ENT_COMPAT,"iso-8859-1");
            echo "<br />";
            echo htmlentities($str,ENT_COMPAT,"utf-8");
            echo "<br />";
            //按转换的编码集还原被转换的HTML实体字符
            echo html_entity_decode(htmlentities($str,ENT_COMPAT,"utf-8"),ENT_COMPAT,"utf-8");
        ?>

显示结果如下:

      <a href='http://www.google.cn'>GoogleÖйú</a>
      Google中国

在程序中指定将“Google中国”转换为iso-8859-1的西欧编码,因此转换汉字会显示为乱码,但并不妨碍被反转换,通过浏览器查看源文件为以下的内容。

注意:转换字符的编码和保存的文件自身编码有关,默认情况下,在PHP文件中我们以ANSⅡ码方式保存,如果要显示utf-8编码的字符,需要将文件转为utf-8编码格式保存。

4.清除HTML标签strip_tags()

格式:string strip_tags(string[,allow])

strip_tags()函数的功能是清理字符串中的所有HTML超文本字符,使它成为一个纯文本字符串。第二个参数中的allow参数是规定允许的标签不会被清除。我们来看下面的代码:

        代码2-121:strip_tags.php- 清除HTML标签
        <?php
            $string = "Email: <a href='spam@21cto.com'>spam@21cto.com</a>";
            echo "原始字符串=".$string."<br />";
            echo "目录字符串=".strip_tags($string);
        ?>

结果所有的HTML标签都会被清除,结果如下:

      原始字符串=Email: spam@21cto.com
      目录字符串=Email: spam@21cto.com

有时,需要将页面中一些不必要的HTML格式标签删除,但是还要保留页面内的超链接,就需要使用第二个参数了。来看下面的脚本实例:

        代码2-122:strip_tags2.php- 清除部分HTML标签
        <?php
            $string = "欢迎光临<a href='http://www.21cto.com/'>思技创想</a><b>互联网完全解决方案
        </b>!";
            echo "原始串=".$string."<br />";
            echo "目标串=".strip_tags($string,"<a>");
        ?>

显示结果,保留了超链接,一些HTML格式标签已被清除。

      原始串=欢迎光临思技创想互联网完全解决方案!
      目标串=欢迎光临思技创想互联网完全解决方案!

5.字符串比较strcmp()与strncmp()

格式:int strcmp (string $str1, string $str2,)

int strncmp (string $str1, string $str2, int $len)

strncmp()函数与strcmp()函数都用于字符串的相似性比较处理,两者有共同之处,但仍有少许不同,strcmp()是比较两个字符串整体是否相同,而strncmp()可以指定从源字符串的某个位置-$len开始比较。我们来看下面的脚本例子:

        代码2-123:strcmp.php使用strcmp函数字符串比较
        <?php
            $stringone = "my story";
            $stringtwo = "my story";
            if (strcmp($stringone , $stringtwo) == 0){
              //等价于if ($stringone == $stringtwo){
              echo "两个字符串相同。<br />";
            }
            //比较前3个字符.
            if (strncmp ("php developer" , "php" , 3) == 0){
                echo "两字符前3个字符是相同的!";
            }
        ?>

6.字符串解析strok()

格式:string strtok (string $str , string $token)

strok()函数用于解析一个字符串,该函数会按照$token参数来解析和跟踪原始字符串。请看下面脚本:

        代码2-124:strok.php使用strok解析字符串
        <?php
          $info = "员工A:staff@21cto.com|思技创想,技术开发部";
          //$info字符串使用了:|和全角逗号3种分隔符,以此为标志分析
          $tokens = ":|,";
          $tokenized = strtok($info , $tokens);
          //从$tokenized数组循环显示每个元素
          while ($tokenized) {
            echo "值 = $tokenized<br>";
            $tokenized = strtok($tokens);
          }
        ?>

运行结果如下:

      值 = 员工A
      值 = staff@21cto.com
      值 = 思技创想
      值 = 技术开发部

7.查找并替换字符串strpbrk()

格式:string strpbrk(string $haystack ,string $char_list)

strbprk函数返回从原始字符串中的标识字符开始到截取到末尾,生成一个新的字符串。如下例:

        代码2-125:strpbrk.php- 使用strpbrk截取字符串
        <?php
            $text = 'Hello,Welcome to PHP5 world';
            echo strpbrk($text , 'W');
            echo "<br />";
            echo strpbrk($text , 'w');
        ?>

显示结果如下:

        Welcome to PHP5 world
        world

我们可以看到,脚本显示的第一次截取是从大写字母W开始之后的字符串,第二次显示是从小写字母w开始的字符串。

strpbrk()函数是PHP 5新增的函数,另外,它对标识字符串的大小写是敏感的。

8.strspn()函数与strcspn()函数

格式:int strspn/strcspn(string $str1,string $str2)

该函数返回str1中包含有str2中字符的第一部分的长度。如:

        echo strspn("hello","oleh");

该表达式将显示5,表示有5个字符在字符串2中包含。请看下面一个实用的脚本例子,它用于检查字符串中的某一段字符串是否属于一个特定的用户名称设定。

        代码2-126:strspn.php- 使用strspn函数校验用户名
        <?php
          $array1 = range(0, 9);
          $array2 = range("a", "z");
          $chars = implode("", $array1) . implode("", $array2);
          $username = "raymond";
            echo "用户名可以使用的合法字符串: $chars<br>";
            if (($n = strspn($username, $chars)) != strlen($username)) {
            echo "长度为$n的字符串 \"$username\". 是一个非法的用户名";
            }
        ?>

这里,我们使用了range()函数,它用来生成一个指定范围的数组,比如0~9 或a~z。接着我们使用implode函数将数组转换为字符串变量$chars,然后我们使用strspn()函数检查$username变量是否在$chars这个字符串中,如果有则表示用户名输入的格式正确,否则输入的是非法的用户名。由此可以看出,这段代码经常用在网站的注册会员登录时,对用户名格式的校验。

我们再看一个使用strspn()函数检验年月日输入的实用例子。

        代码2-127:strspn_check_date.php- 使用strspn()函数校验日期
        <?php
          $year = 2008;
          $month = 12;
          $day = 32;
          //检查年份输入
          $len = strspn($year,"0123456789");
          if ( strlen($year)!=$len ){
            echo "错误的年份";
          }
          //检查月份输入
          $len = strspn($month,"0123456789");
          if ( strlen($month)!=$len ){
            echo "错误的月份";
          }
          if ( $month<1 || $month>12 ){
            echo "错误的月份";
          }
          //根据年份计算某月的天数
          if ( $month==2 ) {
            if ( $year % 4 != 0 ){
              $dpm[2] = 28;
            }else if ( $year % 400 == 0 ){
              $dpm[2] = 29;
            }else if ( $year % 100 == 0 ){
              $dpm[2] = 28;
            }else {
              $dpm[2] = 29;
            }
          }
          //检查天的输入
          $len = strspn($day,"0123456789");
          if ( strlen($day)!=$len ){
            echo "天填写错误";
          }
          if ( $day<1 || $day>$dpm[(int)$month] ) {
            echo "天填写错误";
          }
        ?>

该例将显示“天填写错误”。

strcspn()函数正好与strspn()函数的功能相反,它返回str1中包含str2中所没有字符的第一部分的长度。

如:echo strcspn("hello " ,"by ");

该表达式将返回显示5,表示字符串1中有5个字符不包含在字符串2中。

2.22 日期与时间

PHP在日期时间处理上提供了丰富的函数,此外从PHP 5.1起,PHP开发小组重写并增强了一部分面向对象的日期函数。

时间与日期函数在Web应用上非常广泛,比如计算用户的在线时间,会员续费时间,计算网站在哪个时间访问量大,时间的加减运算等操作。

说明:日期时间处理是PHP内核的一部分,不需要外部扩展库。

2.22.1 UNIX时间戳

时间戳(timestamp)表示自1970年1月1日到现在经过的秒数,包括笔者现在正打字的时间,您在阅读本书的时间都在时间戳的表示范围之中,这个数值的最大值为214748364——直到2038年1月18日。如下代码:

        echo "当前时间戳为:time()";

在该例中,time()函数用以返回一个31位整数数值,表示如文件修改信息的函数,比如date(),filemtime(),mktime(),strtotime()等函数。

时间戳是源于UNIX系统的时间表示方法,如Linux、FreeBSD以及其他类UNIX操作系统,因此称为UNIX时间戳。

2.22.2 日期函数

1.取得和改变默认时区

从PHP 5.1.0开始,内置的日期时间函数已经被重写了,当脚本内未设置时区时,PHP将默认为格林威治标准时间,因此需要使用下列函数:

        date_default_timezone_get()

设定用于一个脚本中所有日期时间函数的默认时区。

下面使用date_default_timezone_set()设置默认时区为中国北京标准时间,脚本如下:

        代码2-128:date_default_timezone_set.php- 设置本地时区
        <?php
            //设置默认时区为中国时区
            date_default_timezone_set('PRC');
            $time = time();
            //把date函数可以接受的参数放在数组中,然后依次显示不同的日期时间格式
            $formats = array(
                            'U' ,
                            'r' ,
                            'c' ,
                            'l , F jS , Y , g:i A' ,
                            'H:i:s D d M y' ,
                            'm/j/y g:i:s a O (T)' ,
                            'Y-m-d H:i:s'
                          );
            foreach($formats as $format){
              //依次显示
              echo "<p><b>$format</b>: " . date($format) . "</p>\n";
            }
        ?>

除了使用本函数外,我们也可以直接修改php.ini中date.timezone值,参数设置如下:

        date.timezone = Etc/GMT-8

代表当前服务器的时区是在东八区,即中国时区。另外date_default_timezone_get()函数返回当前php.ini文件设置的默认时区。使用如下代码:

        echo 'date_default_timezone_set: ' . date_default_timezone_get();

2.date()函数

格式:string date(string format[,int timestamp])

该函数返回format指定的时间或时间格式字符串。第一个参数指定对日期信息进行格式化的方式,第二个参数timestamp是可选的,如果使用timestamp可选参数,则显示时间戳对应的时间,如果没有,则显示当前时间的时间戳,等同于使用time()函数。

date()函数第一个参数format如表2-19所示(请读者注意,该表是按类别组织的)。

表2-19 date()函数的格式化参数表

续表

其中:格式字串中不能被识别的字符将原样显示。Z 格式在使用gmdate() 函数时总是返回0。

我们来看如下代码示例:

        代码2-129:date.php- 显示当前日期时间
        <?php
            echo date("Y-m-d G:i:s");
            echo date('y-n-j');
        ?>

比如当前日期为:2009-06-16 8:08:20,而该脚本输出的时间为0:08:20,这是因为php.ini文件中的时区默认值是格林威治时间,需要修改为东八区或在该脚本的第一句加入:

      date_default_timezone_set('PRC');。

脚本中第二个date()函数输出的是一个简写的日期,如09-6-16。下面我们再用date()函数输出英文的星期名称,脚本如下:

        代码2-130:date2.php- 显示今天和昨天的英文星期
        <?php
            echo "今天是".date(l);
            echo "昨天是" . date("l", time() -86400) . "\n";
        ?>

比如今天是星期六,那么输出内容为:今天是Saturday,昨天是Friday。因为PHP没有输出中文星期的功能,我们可以使用switch分支处理,根据不同的英文日期,对应输出相应的中文星期。

3. getdate()函数

格式:getdate([int $timestamp])

getdate()函数根据给定时间戳$timestamp的值,返回一个完整的日期时间的关联数组。在没有时间戳参数的情况下,返回当前日期与时间。数组中的每个元素都代表日期/时间值中的一个特定组成部分。

可向getdate()函数提交可选的时间戳自变量,以获得与时间戳对应的日期/时间值,应用此函数来获得一系列离散的,容易分离的日期/时间值。请看以下例子说明:

        代码2-131:getdate.php- 使用getdate()函数
        <?php
          $now = time();   //当前时间戳,可换为其他合法的时间戳
          $array = getdate( $now );
          print_r( $array );
       ?>

执行后数组的内容如下:

        Array
        (
          [seconds] => 14
          [minutes] => 48
          [hours] => 9
          [mday] => 25
          [wday] => 1
          [mon] => 1
          [year] => 2010
          [yday] => 24
          [weekday] => Monday
          [month] => January
          [0] => 1264412894
        )

关于该数组内容的说明,请见表2-20。

表2-20 getdate()函数返回的数组说明

4. checkdate()函数

该函数用于检测一个日期格式是否正确,它接受的参数是月,日,年,返回的是一个布尔值。

格式:boolean check(int month,int day,int year);

请看如下脚本:

        代码2-132:checkdate.php- 检测日期格式是否正确
        <?php
            $m = 2;
            $d = 27;
            $y = 2009;
            echo "{$y}年{$m}月{$d}日";
            if (!checkdate($m,$d,$y)) {
              echo '输入的日期错误';
            }else{
              echo '输入的日期正确';
            }
        ?>

该脚本会显示输入的日期正确,可以试将$d日期变量值更改为30,再运行一次,会提示输入的日期错误,因为2009年2月没有30号。

2.22.3 时间函数

1.time()函数

格式:int time( void )

time()函数默认返回系统当前的时间,以UNIX时间戳方式显示。如:

        $now = time();

我们在前面getdate()函数中已经用过该函数。判断当前时间的秒数是奇数还是偶数的脚本如下:

        代码2-133:time.php- 使用time()函数的值判断当前时间戳的奇偶数
        <?php
            $now = time();
            if (($now % 2) == 0) {
              echo "偶数秒";
            } else {
              echo "奇数秒";
            }
        ?>

在该脚本中使用%运算符取余,如果当前时间戳能被2整除,则认为是偶数秒,反之为奇数秒。

time()函数常常与date()函数联合使用,作为date()函数时间参数,以显示指定的日期。如:

        代码2-134: get_tomorow.php- 取得明天的日期
        <?php
          $tomorrow = time()+86400;
          echo "明天日期:".date("Y年m月d日", $tomorrow);
        ?>

该脚本中将今天的时间戳加上一天一夜的时间86400秒,也就成了明天的时间范围,再将得出的值$tomorrow传递给date()函数,data()函数根据传递来的时间戳计算出这个秒数对应的日期。

2.mktime()函数

格式:mktime($hour,$minute,$second,$month,$day,$year)

此函数的作用与getdate()函数的功能正好相对,它由一系列的日期与时间值生成一个UNIX时间戳。不用自变量时,它生成当前日期+时间的UNIX时间戳。

比如我们想取得2010年9月9日的时间戳,代码如下:

        代码2-135:mktime.php取得指定日期的时间戳
        <?php
            //返回时间戳
            echo mktime(0,0,0,9,9,2010);
        ?>

还是看一个较为实用的例子,我们结合data()函数与mktime()函数一起,计算2010年的9月有多少天。

代码如下:

      代码2-132:mktime2.php使用mktime()与date()函数取得指定月份的天数
      <?php
        echo date("t",mktime(0,0,0,date("9"),1,date("2010")));
      ?>

该脚本会显示30,表示2010年的9月份有30天。

3.strtotime()函数

格式:strtotime($str)

此函数可将英文日期/时间字符串转换成UNIX时间戳。应用此函数将非标准化的日期/时间字符串转换成标准、兼容的UNIX时间戳。

下面看一个用strtotime()函数处理日期时产生的几类英文日期的代码。

        代码2-136:strtotime.php- 使用strtotime()函数
        <?php
            //设置默认时区为中国时区
              date_default_timezone_set('PRC');
              echo "<table>";
              $dateArray = array(
                              "now" , "today" , "tomorrow" , "yesterday" ,
                              "Thursday" , "this Thursday" , "last Thursday" ,
                              "+2 hours" , "-1 month" , "+10 minutes" ,
                              "30 seconds" , "+2 years -1 month" , "next week" ,
                              "last month" , "last year" , "2 weeks ago" ,
                              "next Friday"
                            );
              foreach($dateArray as $mydate){
                echo "<tr><td>$mydate:</td><td>" . date('Y-m-d H:I:s',strtotime($mydate)) .
        "</td></tr>\n";
              }
              echo "</table>";
        ?>

显示结果如下:

上面的例子中有许多日常对日期计算的例子,在开发中直接引用即可。

提得一提的是,如果在程序中传递不合法的格式给日期时间函数时,会产生一条E_NOTICE级别的错误信息。

4.strftime()函数

格式:strftime($format , $ts)

strftime()函数将UNIX时间戳格式化成适用于系统当前环境的日期字符串。应用此函数建立与当前环境兼容的日期字符串。

        代码2-137:strftime.php- 格式化显示系统日期
        <?php
          // 设置本地化环境为简体中文
          setlocale(LC_TIME, "zhs");
          echo strftime("Month: %B ");
          echo strftime("Day: %A ");
        ?>

接下来的两个函数都是以格林威治时间来做标准输出,所以都以gm开头,我们来分别了解。

5.gmmktime()函数

格式:gmmktime($hour , $minute, $second , $month , $day,$year)

此函数由一系列格林威治GMT时间表示的日期与时间值生成一个UNIX时间戳。不用自变量时,它生成一个当前GMT即时时间的UNIX时间戳。

        <?php
          //返回一个时间戳 12:25:23 9-Jul-2009
          echo gmmktime(12,25,23,7,9,2009);
        ?>

5.gmdate()函数

格式:gmdate($format,$ts)

此函数将UNIX时间戳格式化成可阅读的日期字符串。此日期字符串以格林威治GMT时间表示。

        <?php
            echo gmdate("d-M-Y h:i A",gmmktime());
        ?>

该脚本会把当前日期转换为格林威治时间显示出来。比如当前日期时间是2010年2月1日上午12:16分,转换后的结果为:

        01-Feb-2010 04:16 AM

8.microtime()函数

在使用搜索引擎查找关键字时,会在结果页面告诉此次搜索的所耗的时间。也可以在PHP程序里加入计算脚本时间的语句,值得一提的是,百度前端用的也是PHP。

这样即可给用户详细的报告,另外也可以知道脚本中有哪些存在速度的瓶颈。

格式:mixed microtime([bool $get_as_float])

microtime()函数返回UNIX时间戳以及GMT时间1970年1月1日到现在所消逝的秒数与微秒数。

在基准特定的代码块,可用该函数准确测量脚本的执行时间。将该段代码放于要计算时间的PHP脚本程序的开头部分。

        function utime (){
            $rtime = explode( " ",microtime());
            $usec = (double)$rtime[0];
            $sec = (double)$rtime[1];
            return $sec + $usec;
        }
        $start = utime();

将下面的代码置于程序的最底部,如下:

        $end = utime();
        $run = $end - $start;
        echo "脚本运行了 " . substr($run,0,5) . " 秒.";

这样,PHP脚本就会显示从头至尾运行耗费了多长时间,如果读者对这种技术感兴趣,还可以浏览PHP的官方手册,有关Microtime()函数的内容(http://www.php.net/microtime)。

2.23 网络相关处理

HTTP协议的英文全称为HyperText Transfer Protocal,即超文本传输协议。用以规定Web服务器和客户端浏览器之间的对话,如传输控制、加密等。

PHP提供大量与网络协议相关的函数,如HTTP信息的处理,表单处理等函数族,下面一起来了解。

2.23.1 header()函数

格式:void header (string $string [, bool $replace = true [, int $http_response_code ]] )

PHP的header()函数允许发送指定的HTTP头信息,主要包括提供非常有用的验证功能以及URL重定向等功能。

使用header()函数很简单,只要一个参数就可以让浏览器完成相应的任务。

1.发送HTTP头信息

一个非常有用的HTTP头叫做“Location:”,使用该关键字作为页面转向的功能。只要在“Location:”后增加一个URL或者相对路径字符串,浏览器就会根据指定的新地址转向,如下例子:

        代码2-138:header_location.php- 使用header函数重定向
        <?php
            if(( isset( $_GET[gender]) && ( $_GET[gender] == "female" )) {
            header( "Location: http://lady.sohu.com" );
            exit;
            }
        ?>

如在该脚本中输入一些参数,如http://localhost/chapter_2/header_location.php?gender=female,浏览器会立即转向到http://lady.sohu.com的网页,地址栏也同样变成该网址。

header函数也支持延时转向的功能,代码如下:

        代码2-139:header_location_refresh.php使用header函数延时重定向
        <?php
          header("refresh:3;url=http://www.php.net");
          echo '正在加载,请稍等...<br>3秒后自动跳转...';
        ?>

这里把Location换成了Refresh,用以指定等待的时间,然后再跳转到url指定的页面,这样可以代替html页面中的meta标识和Javascript的跳转方式。

2.HTTP验证

另一个有用的功能是,可以使用header()函数要求浏览器弹出一个验证窗口,提示输入用户名和密码,这样的验证称为WWW认证,请见下面的例子:

        代码2-140:www-authenticate.php- 使用www认证,验证用户名和密码
        <?php
            $username = 'username';
            $password = 'password';
            if (!isset($_SERVER['PHP_AUTH_USER'])){
              Header( "WWW-Authenticate: Basic realm=\"PHP登录验证\"" );
              Header( "HTTP/1.0401 Unauthorized" );
              echo "操作被中止 \n";
              exit;
            } else {
              if ( ($_SERVER['PHP_AUTH_USER'] == $userame ) && ($_SERVER['PHP_AUTH_PW'] ==
        $password ) ){
                  print( "登录成功<br/>" );
              } else {
                  print( "登录失败<br/>" );
              }
            }
        ?>

该脚本运行时,将在浏览器主窗口弹出下面的弹出式窗口,如图2-18所示。

一旦用户输入信息到这个验证对话框,PHP就会将输入的内容保存到3 个服务器超级变量,分别为$_SERVER['PHP_AUTH_USER'](用以保存用户名)、$_SERVER['PHP_AUTH_PASSWD'](用以保存密码)以及$_SERVER['PHP_AUTH_TYPE',我们可以通过这3 个数组的内容,来验证用户身份是否匹配。

图2-18

除了重定向和WWW认证,还可以使用header()函数来处理客户端缓存,告诉浏览器网页的过期时间(这对网站性能很有帮助),请求是什么,成功与否,还可以让客户端浏览器返回一些代码,下面是header()函数的应用实例:

正常接受客户端请求:

        header('HTTP/1.1200 OK');

发送到客户端一个404无此页面的错误:

        header('HTTP/1.1404 Not Found');

还可以发送500,403等HTTP头信息,有关具体含义请参阅附录G。

禁止浏览器缓存当前HTML内容:

        header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate');
        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
        header('Pragma: no-cache');

另外在做页面重定向时,有时需要向用户提示一段信息,比如等待10秒,然后再做转向,脚本如下:

        header('Refresh: 10; url=http://www.21cto.com/');
        echo '正在转向,请等待10秒钟';

该功能与HTML的META标识符功能相同。

        <meta http-equiv="refresh" content="10;http://www.21cto.com" />

装完Apache或IIS服务器后,这些脚本引擎或虚拟机会在返回给用户的HTTP头信息中增加X-Powered-By的标签,告诉本服务器是由何脚本驱动的,可以使用header()函数屏蔽或更改此信息,如下:

        header('X-Powered-By: PHP/5.2.6');
        header('X-Powered-By: Firefox 3.6');

下面的代码告诉浏览器当前的页面的最后更新时间(对于缓存很有用):

        $time = time() -60;
        header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT');

发送内容的长度(对于缓存很有用):

        header('Content-Length: 1234');

客户请求的文档在其他地方,新的URL在Location头中给出,浏览器自动转向新的URL:

        header("301 Moved Permanently");
        header("Location: http://www.newurl.com/location/");

发送一个下载的,头信息,常用于下载文件:

        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="example.rar"');
        header('Content-Transfer-Encoding: binary');
        //调入文件并发送给浏览器
        readfile('example.rar');

当前文件所使用的语言(en = English,en_us=美国英语,zh-cn为中文):

        header('Content-language: zh-CN');

设置发送网页的内容(MIME)和所使用字符集(文本文件和HTML文档使用):

        header('Content-Type: text/html; charset=gb2312');
        header('Content-Type: text/html; charset=utf-8');
        header('Content-Type: text/plain'); //发送的文件为纯文本文件
        header('Content-Type: image/jpeg'); // JPEG格式图片
        header('Content-Type: application/zip'); // ZIP压缩文件
        header('Content-Type: application/pdf'); // PDF文档
        header('Content-Type: audio/mpeg'); // 音频格式文件 (MP3,wmv.. .)

发送一个Flash文档,表示是一个Flash动画文件,常用于PHP的Ming扩展库。

        header('Content-Type: application/x-shockwave-flash');
2.23.2 处理URL字符串

1.关于HTTP URL

假如有以下的URL字符串:

        http://www.phei.com.cn:80/sort/index.php?browse=al1&category=computer&keyword=php%20bo
        ok#more

下面对该URL字符串做如下详细说明,如表2-21所示。

表2-21

其中,从http://www.phei.com.cn/之后的字符串被称为URI(Uniform Resource Identifier:统一资源标识符),整个字符串被称为URL(Uniform Resoure Locator:统一资源定位器)。

PHP中有相关的函数对URI与URL字符串解的函数,下面分别介绍。

① parse_url() 函数

使用parse_url()函数来解析URL字符串的内容,脚本实例如下:

        代码2-141:parse_url.php- 使用parse_url解析URL字符
        <?php
          $url =
        "http://www.phei.com.cn/sort/index.php?browse=al1&category=computer&keyword=php%20book
        #mor";
          $p = parse_url($url);
          foreach ($p as $name => $content) {
            echo "<b>$name:</b> $content<br>";
          }
        ?>

该脚本的执行结果如下:

      cheme: http
      host: www.phei.com.cn
      path: /sort/index.php
      query: browse=al1&category=computer&keyword=php%20book
      fragment: more

② parse_str()函数

parse_str()函数用于分析URL地址,对URL上使用GET传递的值或对服务器变量Query String进行分析,不必再用explode()函数做切分后再处理,直接使用parse_str()函数就可以把GET传递的变量/值转换为PHP可以使用的变量。请看下面的代码例子:

        代码2-142:parse_str.php- 对URL值对自动分解为变量
        <?php
            /*假设当前的文件名为parstr.php当前访问的URL路径为:
            * http://localhost/chapter-2/parstr.php?username=dujiang&gender=male
            */
            parse_str($_SERVER['QUERY_STRING']);
            //根据参数,自动生成$username和$gender变量和它们的值
            echo $username;
            echo "<br />";
            echo $gender;
        ?>

该脚本的执行结果如下:

        dujiang
        dale

2.URL编码

URL编码(encode)的原理是将URL中的空格,以及不安全的ASCII字符以及双字节字符(如汉字,日文等)转换为以%开头的十六进制编码,关于字符安全的定义还可以查看rfc2396(网址:http://www.ietf.org/rfc/rfc2396.txt),它们包括7位的ASCII字符和上档字符"-_.!~*'()"。

上例中20%这类URL编码,它代表空格字符,如果从表单进行GET或POST传递数据时,浏览器会自动进行编码,然后再进行数据传递操作。

但是,有时在超链接上传递长的字符串,特别是字符串中含有汉字时,就需要用urlencode()函数对传递的字符串进行URL编码,避免在数据传递时数据丢失或出错。

然后在接受传递的脚本中使用urldecode()函数对传来的字符进行解码(decode)即可。

请看下面的脚本例子:

        代码2-143:url_encode.php- 使用url_encode()对URL中的汉字编码
        <?php
            $AuthorName = "杜江";
            $EncodeAuthor = urlencode($AuthorName);
            $BookName = "PHP5与MySQL5 Web开发技术详解";
            $EncodeBookName = urlencode($BookName);
            echo  "<a href='urldecode.php?author={$EncodeAuthor}&book={$EncodeBookName}'>推荐PHP
        新书</a>";
        ?>

该脚本执行后会显示一个“推荐PHP新书”的超链接,可以注意到链接的字符串,变成为以下编码后的字符串。

        http://localhost/chapter-2/urldecode.php?Author=%B6%C5%BD%AD&BookName=PHP5%D3%EBMySQL5
        +Web%BF%AA%B7%A2%BC%BC%CA%F5%CF%EA%BD%E2

可以看到,汉字以及空格已经被编码,这样不仅可以实现数据的完整性,同时也保障了一定安全。下面使用urldecode()函数将传递来的字符值还原为原内容,脚本如下:

        代码2-144:url_decode.php- 使用url_decode()函数对URL编码中的汉字进行解码
        <?php
            $AuthorName = urldecode($_GET['author']);
            $BookName=urldecode($_GET['BoodName']);
            echo "书名:{$BookName}<br />";
            echo "作者:{$AuthorName}<br />";
        ?>

运行结果如图2-19所示。

图2-19

除了上述两个函数外,还可以使用base64_encode()和base64_decode()函数来进行URL编码,使用方法和urlencode()函数相似,详情请查阅PHP官方手册。

2.23.3 获得PHP环境信息

查看当前加载的PHP扩展库,可以使用get_loaded_extension()函数和get_extension_funs()函数,查看哪些函数以及扩展库已经打开启用。

这两个函数没有参数,get_extension_funcs()函数用以返回某个扩展所包含的全部函数,以数组形式体现。get_loaded_extensions()函数用以返回所有编译模块和加载的扩展库名称。

使用get_loaded_extensions ()函数的PHP脚本例子如下:

        代码2-145:get_loaded_extensions.php- 取得已经调入的PHP扩展
        <?php
            $extensions = get_loaded_extensions();
            //循环显示该数组
            foreach($extensions as $extension) {
              echo $extension;
              echo ' (', implode(', ', get_extension_funcs($extension)), ')<BR />';
            }
        ?>

取得某个扩展库提供的方法或函数,代码如下:

        代码2-146:get_extension_func.php- 取得某个扩展库中提供的全部函数或方法
        <?php
            $ext_funcs=get_extension_funcs('mysqli');
            foreach($ext_funcs as $funcs){
              echo"<li>$funcs";
              echo"</ul>";
            }
        ?>

该脚本会把mysqli扩展提供的所有函数全部打印出来,这对全面了解一个PHP扩展库很有帮助。

2.24 其他操作符

2.24.1 @-错误抑制符

在常见的数据库连接与文件创建操作或出现除0 等异常操作时,可以用@符号来抑制函数错误信息输出到浏览器端,例子如下:

        $a = @(50/0);
        @mysql_query($sql);
        @mkdir('/usr/1/2'');

这样普通用户在浏览器中就不会看到一些比较难懂的错误信息了。但是过多的使用该符号,也会给程序的调试带来一些困扰,如一些错误信息不能被显示,但并不知道错误出在什么地方等,这点请读者注意。

2.24.2 执行外部命令

使用``来运行外部系统命令,注意不是单引号,是键盘左上角ESC键下方的上档键。这种操作在PHP中被称为“backticks”,注意它执行的命令和外部运行的操作系统有关。请见下面的脚本例子:

        代码2-147:run_external_command.php  - 运行外部命令
        <?php
            $out = `dir c:`;         //适用于windows或部分linux系统
            print_r($out);
        ?>

该结果将得到用DOS外部命令dir列出C盘根目录下的子目录和文件信息。当然这个脚本只能在Windows下运行,下面使用UNIX或Linux下的命令进行目录列表。

        代码2-148:run_external_command2.php- 运行外部命令
        <?php
            $out = `ls –al`;     //适用于UNIX或linux系统
            echo $out;
        ?>

也可以使用shell_exec()函数来执行该任务,如下例:

        代码2-149:shell_exec.php- 使用shell_exec()函数运行外部命令
        <?php
            $out = shell_exec("dir");
            echo $out;
        ?>

两个函数得到的结果是相同的,有所区别的是使用`符号会将结果置于一个数组中,第二种shell_exec方法是将结果置于一个标量变量中。

注意,在UNIX系统运行一些系统级别的外部命令时,需要PHP脚本具有root执行权限,即文件本身的所有者(owner)是管理员(wheel)组用户。

2.25 小结

本章描述了PHP的基本语言特性,包括常量,变量,控制结构以及常用的内部函数等。

另外还介绍了在PHP中对字符串的操作和处理,以及控制PHP的输出和格式化等。

有的内容似乎看起来很简单,但能够熟悉掌握也非一日之功,请牢记“勿以浮砂筑高台”,希望各位读者多读多练多思考,掌握好牢固的基本功后,再结合本章的内容开发出更实用更有效率的程序。